是一个靠谱的协议
不可靠的
#include
#include
#include
#include
#include
#include
#include
//以上四个头文件经常会用
const uint16_t port = 8080;
//udp_server
int main()
{
//1. 创建套接字,打开网络文件
//第一个参数作用域对应IPV4,第二个类型对应udp的数据报,第三个协议类型设为0
int sock = socket(AF_INET,SOCK_DGRAM,0);
if(sock<0)
{
std::cerr<<"socket creat error: "<<errno<<std::endl;
return 1;
}
//给该服务器绑定端口和ip(特殊处理)
struct sockaddr_in local;
local.sin_family = AF_INET;//对应IPV4
local.sin_port = htons(port);//此处的端口号,是我们计算机上的变量,是主机序列
// a. 需要将人识别的点分十进制,字符串风格的IP地址,转化成四字节整数IP
// b. 也要考虑大小端
//坑:
//云服务器,不允许用户直接bind公网ip,另外,正常编写的时候,我们也不会指明IP
//INADDR_ANY: 如果你的bind是确定的IP(主机),以为者发到该IP主机上面的数据才会交给
//你的网络进程,但是,一般服务器配置有多张网卡,配置多个IP,我们需要的不是某个IP上面的
//数据,我们需要的是,所有发送到该主机,发送到该端口的数据
local.sin_addr.s_addr = INADDR_ANY;
//2. bind
//绑定成功=0,绑定失败<0
//local本地已经配置好了,传进去
if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
{
std::cerr<<"bind error : "<<errno<<std::endl;
return 2;
}
//3. 提供服务
//所有网络服务器都是一个死循环
//如果是udp,不可以通过文件的形式读取,有专门的接口
bool quit = false;
#define NUM 1024
char buffer[NUM];
while(!quit)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
//接收客户端消息
recvfrom(sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);
std::cout<<"client# "<<buffer<<std::endl;
std::string echo_hello = "hello";
sendto(sock,echo_hello.c_str(),echo_hello.size(),0,(struct sockaddr*)&peer,len);
}
return 0;
}
#include
#include
#include
#include
#include
#include
#include
void Usage(std::string proc)
{
std::cout << "Usage: \n\t" << proc << "server_ip server_port" << std::endl;
}
//./udp_client server_ip server_port
int main(int argc, char *argv[])
{
if (argc != 3)
{
Usage(argv[0]);
return 0;
}
// 1.创建套接字,打开网络文件
// IPV4协议,udp数据包,默认方式
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0)
{
//如果创建失败,就显示错误信息
std::cerr << "socket error :" << errno << std::endl;
return 1;
}
//客户端需要显示的bind吗?
// a. 首先,客户端也要有ip和port
// b. 但是,客户端不需要显示的bind。一旦显示的bind,就要明确,client和哪一个port关联
// client指明的端口号,在client端一定会有吗?有可能被占用,就导致client无法使用
// server要的是port必须明确,而且不变,但client只要有即可,一般是由OS自动给你bind()
//就是client正常发送数据的时候,OS自动给你bind,采用的是随机端口的方式
// b. 要发给谁?
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(atoi(argv[2]));
server.sin_addr.s_addr = inet_addr(argv[1]); // IP地址
// 2.使用服务
while (1)
{
// a. 数据从哪来
std::string message;
std::cout << "输入# ";
std::cin >> message;
//客户端发送
sendto(sock,message.c_str(),message.size(),0,(struct sockaddr*)&server,sizeof(server)) ;
//此处的tmp就是一个占位符而已
struct sockaddr_in tmp;
socklen_t len = sizeof(tmp);
char buffer[1024];
recvfrom(sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&tmp,&len);
std::cout<<"server echo#"<<buffer<<std::endl;
}
return 0;
}
.PHONY:all
all:udp_server udp_client
udp_server:udp_server.cc
g++ -o $@ $^ -std=c++11
udp_client:udp_client.cc
g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
rm -f udp_server udp_client
#include
#include
#include
#include
#include
#include
#include
//以上四个头文件经常会用
// const uint16_t port = 8080;
std::string Usage(std::string proc)
{
std::cout << proc << " port" << std::endl;
}
// udp_server
//./udp_server port
int main(int argc, char *argv[])
{
if (argc != 2)
{
Usage(argv[0]);
return -1;
}
uint16_t port = atoi(argv[1]);
// 1. 创建套接字,打开网络文件
//第一个参数作用域对应IPV4,第二个类型对应udp的数据报,第三个协议类型设为0
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0)
{
std::cerr << "socket creat error: " << errno << std::endl;
return 1;
}
//给该服务器绑定端口和ip(特殊处理)
struct sockaddr_in local;
local.sin_family = AF_INET; //对应IPV4
local.sin_port = htons(port); //此处的端口号,是我们计算机上的变量,是主机序列
// a. 需要将人识别的点分十进制,字符串风格的IP地址,转化成四字节整数IP
// b. 也要考虑大小端
//坑:
//云服务器,不允许用户直接bind公网ip,另外,正常编写的时候,我们也不会指明IP
// INADDR_ANY: 如果你的bind是确定的IP(主机),以为者发到该IP主机上面的数据才会交给
//你的网络进程,但是,一般服务器配置有多张网卡,配置多个IP,我们需要的不是某个IP上面的
//数据,我们需要的是,所有发送到该主机,发送到该端口的数据
local.sin_addr.s_addr = INADDR_ANY;
// 2. bind
//绑定成功=0,绑定失败<0
// local本地已经配置好了,传进去
if (bind(sock, (struct sockaddr *)&local, sizeof(local)) < 0)
{
std::cerr << "bind error : " << errno << std::endl;
return 2;
}
// 3. 提供服务
//所有网络服务器都是一个死循环
//如果是udp,不可以通过文件的形式读取,有专门的接口
bool quit = false;
#define NUM 1024
char buffer[NUM];
while (!quit)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
//接收客户端消息
ssize_t cnt = recvfrom(sock, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
if (cnt > 0)
{
buffer[cnt] = 0; // 0 == '\0'
FILE *fp = popen(buffer, "r");
std::string echo_hello;
char line[1024] = {0};
while (fgets(line, sizeof(line), fp) != NULL)
{
echo_hello += line;
}
pclose(fp);
std::cout << "client# " << buffer << std::endl;
echo_hello += "...";
sendto(sock, echo_hello.c_str(), echo_hello.size(), 0, (struct sockaddr *)&peer, len);
}
}
return 0;
}
#include
#include
#include
#include
#include
#include
#include
void Usage(std::string proc)
{
std::cout << "Usage: \n\t" << proc << " server_ip server_port" << std::endl;
}
//./udp_client server_ip server_port
int main(int argc, char *argv[])
{
if (argc != 3)
{
Usage(argv[0]);
return 0;
}
// 1.创建套接字,打开网络文件
// IPV4协议,udp数据包,默认方式
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0)
{
//如果创建失败,就显示错误信息
std::cerr << "socket error :" << errno << std::endl;
return 1;
}
//客户端需要显示的bind吗?
// a. 首先,客户端也要有ip和port
// b. 但是,客户端不需要显示的bind。一旦显示的bind,就要明确,client和哪一个port关联
// client指明的端口号,在client端一定会有吗?有可能被占用,就导致client无法使用
// server要的是port必须明确,而且不变,但client只要有即可,一般是由OS自动给你bind()
//就是client正常发送数据的时候,OS自动给你bind,采用的是随机端口的方式
// b. 要发给谁?
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(atoi(argv[2]));
server.sin_addr.s_addr = inet_addr(argv[1]); // IP地址
// 2.使用服务
while (1)
{
// // a. 数据从哪来
// std::string message;
// std::cout << "输入# ";
// std::cin >> message;
std::cout<< "My Shell $ ";
char line[1024];
fgets(line,sizeof(line),stdin);
//客户端发送
sendto(sock,line,strlen(line),0,(struct sockaddr*)&server,sizeof(server)) ;
//此处的tmp就是一个占位符而已
struct sockaddr_in tmp;
socklen_t len = sizeof(tmp);
char buffer[1024];
ssize_t cnt = recvfrom(sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&tmp,&len);
if(cnt > 0)
{
//在网络通信中,只有报文大小,或者是字节流中字节的个数,没有C/C++字符串这样的概念(虽然后续可能有类似情况)
buffer[cnt] = 0;
std::cout<<"server echo#"<<buffer<<std::endl;
}
}
return 0;
}
accept获取上来新的链接
tcp也有的listen监听状态,backlog是相应套接字排队的最大连接个数
read第一个参数文件描述符,第二个读的数据放到缓冲区里,第三个期望读多少字节
第一个参数识别服务器套接字,第二个参数保存套接字对应的“地方”(客户端IP和端口信息),第三个参数是“地方”的占地大小,返回值对应套接字表示,返回值小于0就是出现了error
bezero 把一段缓冲区清0,不太推荐,等同于memset的功能
获取IP地址
.PHONY:All
All:tcp_client tcp_server
tcp_client:tcp_client.cc
g++ -o $@ $^ -std=c++11
tcp_server:tcp_server.cc
g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
rm -f tcp_client tcp_server
#pragma once
#include
#include
#include
namespace ns_task
{
class Task
{
private:
int sock;
public:
Task() {}
Task(int _sock) : sock(_sock) {}
int Run()
{
char buffer[1024];
memset(buffer, 0, sizeof(buffer));
ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
if (s > 0) //读到数据了
{
buffer[s] = 0; //将获取的内容变成字符串
std::cout << "client# " << buffer << std::endl;
//拉取逻辑
std::string echo_string = ">>>server<<<,";
echo_string += buffer;
write(sock, echo_string.c_str(), echo_string.size());
}
else if (s == 0) //读完了
{
std::cout << "client quit ..." << std::endl;
// break;
}
else // s<0 出错了
{
std::cerr << "read error" << std::endl;
//break;
}
close(sock);
}
~Task(){}
};
}
#pragma once
#include
#include
#include
#include
#include
namespace ns_threadpool
{
const int g_num = 5;
template <class T>
class ThreadPool
{
private:
int num_;
std::queue<T> task_queue_; //该成员是一个临界资源
pthread_mutex_t mtx_;
pthread_cond_t cond_;
static ThreadPool<T> *ins;
private:
// 构造函数必须得实现,但是必须的私有化
ThreadPool(int num = g_num) : num_(num)
{
pthread_mutex_init(&mtx_, nullptr);
pthread_cond_init(&cond_, nullptr);
}
ThreadPool(const ThreadPool<T> &tp) = delete;
//赋值语句
ThreadPool<T> &operator=(ThreadPool<T> &tp) = delete;
public:
static ThreadPool<T> *GetInstance()
{
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
// 当前单例对象还没有被创建
if (ins == nullptr) //双判定,减少锁的争用,提高获取单例的效率!
{
pthread_mutex_lock(&lock);
if (ins == nullptr)
{
ins = new ThreadPool<T>();
ins->InitThreadPool();
std::cout << "首次加载对象" << std::endl;
}
pthread_mutex_unlock(&lock);
}
return ins;
}
void Lock()
{
pthread_mutex_lock(&mtx_);
}
void Unlock()
{
pthread_mutex_unlock(&mtx_);
}
void Wait()
{
pthread_cond_wait(&cond_, &mtx_);
}
void Wakeup()
{
pthread_cond_signal(&cond_);
}
bool IsEmpey()
{
return task_queue_.empty();
}
public:
// 在类中要让线程执行类内成员方法,是不可行的!
// 必须让线程执行静态方法
static void *Rountine(void *args)
{
pthread_detach(pthread_self());
ThreadPool<T> *tp = (ThreadPool<T> *)args;
while (true)
{
tp->Lock();
while (tp->IsEmpey())
{
//任务队列为空,线程该做什么呢??
tp->Wait();
}
//该任务队列中一定有任务了
T t;
tp->PopTask(&t);
tp->Unlock();
t.Run();
}
}
void InitThreadPool()
{
pthread_t tid;
for (int i = 0; i < num_; i++)
{
pthread_create(&tid, nullptr, Rountine, (void *)this /*?*/);
}
}
void PushTask(const T &in)
{
Lock();
task_queue_.push(in);
Unlock();
Wakeup();
}
void PopTask(T *out)
{
*out = task_queue_.front();
task_queue_.pop();
}
~ThreadPool()
{
pthread_mutex_destroy(&mtx_);
pthread_cond_destroy(&cond_);
}
};
template <class T>
ThreadPool<T> *ThreadPool<T>::ins = nullptr;
} // namespace ns_threadpool
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "thread_pool.hpp"
#include "Task.hpp"
using namespace ns_task;
using namespace ns_threadpool;
void Usage(std::string proc)
{
std::cout << proc << " port" << std::endl;
}
// void *HandlerRequest(void *args)
// {
// pthread_detach(pthread_detach(pthread_self()));
// int sock = *(int *)args;
// delete (int *)args;
// ServiceIO(sock);
// close(sock);
// }
// void ServiceIO(int new_sock)
// {
// while (true)
// {
// char buffer[1024];
// memset(buffer, 0, sizeof(buffer));
// ssize_t s = read(new_sock, buffer, sizeof(buffer) - 1); //不用读\0
// if (s > 0) //读到消息了
// {
// buffer[s] = 0; //将读取的内容当成字符串
// std::cout << "client# " << buffer << std::endl;
// std::string echo_string = ">>>server<<<, ";
// echo_string += buffer;
// // write是返回消息
// write(new_sock, echo_string.c_str(), echo_string.size());
// }
// else if (s == 0) //读完了
// {
// std::cout << "client quit ..." << std::endl;
// break;
// }
// }
// }
//./tcp_server 8081
int main(int argc, char *argv[])
{
//启动服务器只要两个参数即可
if (argc != 2)
{
Usage(argv[0]);
return 1;
}
// 1. 创建套接字
int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
if (listen_sock < 0)
{
std::cerr << "socket error: " << errno << std::endl;
return 2;
}
// 2. bind
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET; // IPV4
local.sin_port = htons(atoi(argv[1]));
local.sin_addr.s_addr = INADDR_ANY; //一台机器有多个网卡,无论哪个网卡传进来都能被收到
if (bind(listen_sock, (struct sockaddr *)&local, sizeof(local)) < 0)
{
std::cerr << "bind error : " << errno << std::endl;
return 3;
}
// 3. 因为tcp是面向连接的, a. 在通信前,需要建立连接 b. 才能进行通信
// 一定有人主动建立(客户端,需要服务),一定有人被动接收服务(服务器,提供服务)
// 我们当前写的是一个server,周而复始的不断的等待客户的到来
// 我们要不断提供给用户一个建立连接的功能
// 设置套接字是listen状态,本质是允许用户连接
const int back_log = 5; // 套接字排队的最大连接数
if (listen(listen_sock, back_log) < 0)
{
std::cerr << "listen error" << std::endl;
return 4;
}
// signal(SIGCHLD, SIG_IGN); //在linux中父进程忽略子进程的SIGCHLD信号,子进程会自动退出释放资源
for (;;)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int new_sock = accept(listen_sock, (struct sockaddr *)&peer, &len);
if (new_sock < 0)
{
continue;
}
uint16_t cli_port = ntohs(peer.sin_port);
std::string cli_ip = inet_ntoa(peer.sin_addr);
//提供服务
// version 1 :无人使用的单进程版本
// ServiceIO(new_sock);
std::cout << "get a new link ..." << new_sock << std::endl;
// 1.构建一个新的任务
Task t(new_sock);
// 2.将任务push到后端的线程池
ThreadPool<Task>::GetInstance()->PushTask(t);
// vision4:
// vision 2,3: a. 创建线程,进程无上限 b. 客户的链接来了,我们才给用户创建进程/线程
// //vision 3: 曾经被主线程打开的fd,新线程是否能看到,是否共享?能
// pthread_t tid;
// int* pram = new int(new_sock);
// pthread_create(&tid,nullptr,HandlerRequest,pram);
// version 2 版本
// pid_t id = fork(); //创建一个子进程
// if (id < 0) //创建失败的情况
// {
// continue;
// }
// else if (id == 0)
// {
// close(listen_sock);//子进程关闭,不会影响父进程
// if(fork() > 0) exit(0);
// ServiceIO(new_sock);
// close(new_sock);//服务用完了,文件描述符就不用了,我们就把它关掉
// exit(0);
// }
// else
// {
// // father
// // do noithing
// waitpid(id,nullptr,0);
// close(new_sock);
// }
}
return 0;
}
#include
#include
#include
#include
#include
#include
void Usage(std::string proc)
{
std::cout << proc << " server_ip server_port" << std::endl;
}
// tcp_client server_ip server_port
int main(int argc, char *argv[])
{
if (argc != 3)
{
Usage(argv[0]);
return 1;
}
std::string svr_ip = argv[1];
uint16_t svr_port = atoi(argv[2]);
// 1. 创建socket
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) //创建失败的情况
{
std::cerr << "socket error: " << std::endl;
return 2;
}
// client无需显示的bind,client->server
// client -> connect
struct sockaddr_in server;
bzero(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(svr_port);
server.sin_addr.s_addr = inet_addr(svr_ip.c_str()); //该函数做两件事情 a.将点分十进制的字符串风格的IP,转化成4字节IP b. 将4字节IP转化为网络序列
if (connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0)
{
std::cout << "connect server failed !" << std::endl;
return 3;
}
std::cout << "connect success!" << std::endl;
// 进行正常的业务请求了
while (true)
{
std::cout << "Please Enter# ";
char buffer[1024];
fgets(buffer, sizeof(buffer)-1, stdin);
write(sock, buffer, strlen(buffer));
ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
if (s > 0)
{
buffer[s] = 0;
std::cout << "server echo# " << buffer << std::endl;
}
}
return 0;
}