网站推广的具体方案购物网站二级店铺mvc
- 作者: 五速梦信息网
- 时间: 2026年03月21日 07:26
当前位置: 首页 > news >正文
网站推广的具体方案,购物网站二级店铺mvc,成都网站建设网络公司,北京电商网站建设哪家好文章目录 网络编程套接字(3)4. 简单的TCP网络程序4.1 服务端创建(1) 创建套接字(2) 绑定端口(3) 监听(4) 获取新连接(5) 处理读取与写入 4.2 客户端创建(1)连接服务器 4.3 代码编写(1) v1简单发送消息(2) v2_多进程版本(3) v3_多线程版本(4) v4_线程池版本 网络编程套接字(3)… 文章目录 网络编程套接字(3)4. 简单的TCP网络程序4.1 服务端创建(1) 创建套接字(2) 绑定端口(3) 监听(4) 获取新连接(5) 处理读取与写入 4.2 客户端创建(1)连接服务器 4.3 代码编写(1) v1简单发送消息(2) v2_多进程版本(3) v3_多线程版本(4) v4_线程池版本 网络编程套接字(3)
- 简单的TCP网络程序
4.1 服务端创建
(1) 创建套接字
还是之前udp部分的socket函数这里只是简单说明一下与udp的差异
int socket(int domain, int type, int protocol);只需将第二个参数type换成 SOCK_STREAM: 基于TCP的网络通信流式套接字提供的是流式服务(对应TCP的特点面向字节流)
(2) 绑定端口
还是和之前一样的接口
(3) 监听
UDP服务器的初始化操作只有2步第一步创建套接字第二步是绑定。但是TCP服务器是面向连接的客户端在正式向TCP服务器发送数据之前需要先与TCP服务器建立连接然后才能与服务器进行通信。
因此TCP服务器需要时刻注意是否有客户端发来连接请求此时就需要将TCP服务器创建的套接字设置为监听状态 listen for connections on a socket: 监听套接字上的连接
头文件:#include sys/types.h #include sys/socket.h函数原型:int listen(int sockfd, int backlog);参数说明:第一个参数sockfd: 需要设置为监听状态的套接字对应的文件描述符第二个参数backlog: 这里当成一个整数,后续详细解释返回值:监听成功: 返回0监听失败: 失败返回-1并设置错误码(4) 获取新连接
客户端有新链接到来服务端可以获取到新链接这一步需要死循环获取客户端新链接。 accept a connection on a socket: 接收套接字上的连接
头文件:#include sys/types.h #include sys/socket.h函数原型:int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);参数说明:第一个参数sockfd: 监听套接字第二个参数addr: 获取对方一端网络相关的属性信息第三个参数addrlen: addr的长度返回值:连接成功: 返回接收到的套接字的文件描述符连接失败: 失败返回-1并设置错误码关于accept的返回值: 也是一个文件描述符
为什么又返回一个新的文件描述符返回的这个新的文件描述符跟旧的文件描述符_sockfd有什么关系
感性理解: 对比listen监听套接字与accept函数返回的套接字
listen监听套接字用于获取客户端发来的连接请求。accept函数会不断从监听套接字当中获取新连接accept函数返回的套接字用于为本次accept获取到的连接提供服务。而listen监听套接字的任务只是不断获取新连接而真正为这些连接提供服务的套接字是accept函数返回的套接字而不是监听套接字。
(5) 处理读取与写入
因为TC 提供的是流式服务所以这里利用read和write来实现读取与写入
4.2 客户端创建
4步创建套接字客户端向服务器发起连接请求bind(不需要自己绑定由OS自动分配)处理数据读取与写入
(1)连接服务器 initiate a connection on a socket: 在套接字上发起连接
头文件:#include sys/types.h#include sys/socket.h函数原型:int connect(int sockfd, const struct sockaddr addr, socklen_t addrlen);参数说明:第一个参数sockfd: 表示通过该套接字发起连接请求第二个参数addr: 对方一端网络相关的属性信息第三个参数addrlen: addr的长度返回值连接成功: 返回0连接失败: 失败返回-1并设置错误码4.3 代码编写
这里一共提供4个版本的tcp代码
err.hpp这个代码是公用的后续不在给出
#pragma onceenum
{USAGE_ERR1,SOCKET_ERR,BIND_ERR,LISTEN_ERR,CONNECT_ERR,
};(1) v1__简单发送消息
客户端向服务端发送消息服务端收到后再把消息发回给客户端
tcpServer.hpp
#includeiostream
#includesys/types.h
#includesys/socket.h
#includenetinet/in.h
#includearpa/inet.h
#includecstring
#includeunistd.h
#includememory
#includefunctional
#includeerr.hpp
using namespace std;// 问题: 目前的服务器, 无法处理多个client的问题, 为什么?
// 单进程服务, 当服务端向客户端提供业务处理服务时, 没有办法accet, 不能处理连接namespace ns_server
{static const uint16_t defaultport8081;static int backlog32;using func_tfunctionstring(const string); // 回调函数,一种处理逻辑class TcpServer{public:TcpServer(func_t func, uint16t portdefaultport):func(func),port(port),quit(true){}void InitServer(){// 1. 创建socket文件listensock_socket(AF_INET,SOCK_STREAM,0);if(listensock_0){cerrcreate socket errorendl;exit(SOCKET_ERR);}// 2. bindstruct sockaddr_in local;memset(local,0,sizeof(local));local.sinporthtons(port);local.sin_familyAF_INET;local.sin_addr.s_addrINADDRANY;int nbind(listensock,(struct sockaddr)local,sizeof(local));if(n0){cerrbind socket errorendl;exit(BINDERR);}// 3. 监听int mlisten(listensock,backlog);if(m0){cerrlisten socket errorendl;exit(LISTEN_ERR);}}void Start(){quitfalse;while(!quit){struct sockaddr_in client;socklent lensizeof(client);// 4. 获取连接, acceptint sockaccept(listensock,(struct sockaddr*)client,len);if (sock 0) // accept失败并不会终止进程, 只要获取下一个连接{cerr accept error endl;continue;}// 提取client信息 — debugstring clientipinet_ntoa(client.sin_addr); // 把4字节对应的IP转化成字符串风格uint16_t clientportntohs(client.sinport); // 网络序列转主机序列// 5. 获取新连接成功后, 开始进行业务处理cout获取新连接成功: sock from listensock, clientip - clientportendl;// v1service(sock,clientip,clientport);}}// 流式 - 利用read和writevoid service(int sock, const stringclientip,const uint16_t clientport){string whoclientip - to_string(clientport);char buffer[1024];while(true){ssizet sread(sock,buffer,sizeof(buffer)-1);if(s0){buffer[s]0;string resfunc(buffer); // 进行回调coutwho resendl;// 把收到的消息返回(写给客户端)write(sock,res.c_str(),res.size());}else if(s0){// 对方将连接关闭了close(sock);cout who quit, me tooendl;break;}else{close(sock);cerrread error: strerror(errno)endl;break;}}}~TcpServer(){}private:uint16t port;int listensock;bool quit; // 标志服务器是否运行字段funct func;};
}tcpServer.cc
#includetcpServer.hpp
using namespace ns_server;// ./tcp_server port// 使用手册
static void usage(string proc)
{coutusage:\n\tproc port\nendl;
}string echo(const stringmessage)
{return message;
}int main(int argc,char*argv[])
{if(argc!2){usage(argv[0]);exit(USAGE_ERR);}uint16_t portatoi(argv[1]);unique_ptrTcpServer tsvr(new TcpServer(echo,port));tsvr-InitServer();tsvr-Start();return 0;
}tcpClient.cc
#includeiostream
#includesys/types.h
#includesys/socket.h
#includenetinet/in.h
#includearpa/inet.h
#includecstring
#includeunistd.h
#includememory
#includefunctional
#includeerr.hpp
using namespace std;static void usage(string proc)
{coutusage:\n\tproc serverip serverport\n endl;
}// ./tcp_client serverip serverport
int main(int argc,charargv[])
{// 准备工作if(argc!3){usage(argv[0]);exit(USAGE_ERR);}string serveripargv[1];uint16_t serverportatoi(argv[2]);// 1.创建套接字int socksocket(AF_INET,SOCK_STREAM,0);if (sock 0){cerr create socket error: strerror(errno) endl;exit(SOCKET_ERR);}// (2) 客户端要不要bind呢? 要// 要不要自己bind呢? 不要, 因为client要让OS自动给用户进行bind// (3) 要不要listen?不要, 客户端连别人, 永远都是别人listen; 要不要accept?不要, 服务器来连接// 2. connect 客户端向服务器发起连接请求struct sockaddr_in server;memset(server,0,sizeof(server));server.sin_porthtons(serverport);server.sin_familyAF_INET;// server.sin_addr.s_addrINADDR_ANY; 绝对不是inet_aton(serverip.c_str(),(server.sin_addr)); // 字符串风格ip转成点分十进制int cnt5;while(connect(sock,(struct sockaddr)server,sizeof(server))!0) // 连接失败{sleep(1);cout正在给你重连, 重连次数还有: cnt–endl;if(cnt0)break;}if(cnt0){cerr连接失败endl;exit(CONNECT_ERR);}char buffer[1024];// 3. 连接成功while(true){string line;coutEnter ;getline(cin,line);write(sock,line.c_str(),line.size());ssize_t s read(sock, buffer, sizeof(buffer)-1);if (s 0){buffer[s] 0;coutserver echo bufferendl;}else if (s 0){cerr server quit endl;break;}else{cerr read error: strerror(errno) endl;break;}}close(sock);return 0;
}运行结果: (2) v2_多进程版本
v2版本是把单执行流服务器改成多进程版的服务器 在accept获取新连接成功后fork创建创建子进程此时子进程对外提供服务, 父进程只进行accept 父进程的文件描述符会被子进程继承但并不是父子共用同一张文件描述符表因为子进程会拷贝继承父进程的文件描述符表 对于套接字文件也是相同的父进程创建的子进程也会继承父进程的套接字文件此时子进程就能够对特定的套接字文件进行读写操作进而完成对对应客户端的服务
关于阻塞等待与非阻塞等待 若采用阻塞式等待那么服务端还是需要等待服务完当前客户端才能继续获取下一个连接请求此时服务端仍然是以一种串行的方式为客户端提供服务若采用非阻塞式等待虽然在子进程为客户端提供服务期间服务端可以继续获取新连接但此时服务端就需要将所有子进程的PID保存下来并且需要不断花费时间检测子进程是否退出由此可见两种都有缺陷所以我们可以考虑让服务端不等待子进程退出 常见的方式有两种 捕捉SIGCHLD信号将其处理动作设置为忽略。让父进程创建子进程子进程再创建孙子进程子进程退出让孙子进程为客户端提供服务孙进程的回收工作由OS来承担 下面是创建孙进程的方案 tcpServer.hpp #includeiostream #includesys/types.h #includesys/socket.h #includenetinet/in.h #includearpa/inet.h #includecstring #includeunistd.h #includememory #includefunctional #includesys/wait.h #includeerr.hpp using namespace std;namespace ns_server {static const uint16_t defaultport8081;static int backlog32;using func_tfunctionstring(const string); // 回调函数,一种处理逻辑class TcpServer{public:TcpServer(func_t func, uint16t portdefaultport):func(func),port(port),quit(true){}void InitServer(){// 1. 创建socket文件listensock_socket(AF_INET,SOCK_STREAM,0);if(listensock_0){cerrcreate socket errorendl;exit(SOCKET_ERR);}// 2. bindstruct sockaddr_in local;memset(local,0,sizeof(local));local.sinporthtons(port);local.sin_familyAF_INET;local.sin_addr.s_addrINADDRANY;int nbind(listensock,(struct sockaddr)local,sizeof(local));if(n0){cerrbind socket errorendl;exit(BINDERR);}// 3. 监听int mlisten(listensock,backlog);if(m0){cerrlisten socket errorendl;exit(LISTEN_ERR);}}void Start(){// signal(SIGCHLD,SIG_IGN); // ok, 最推荐// signal(SIGCHLD,handler); // 回收子进程, 不太推荐quitfalse;while(!quit){struct sockaddr_in client;socklent lensizeof(client);// 4. 获取连接, acceptint sockaccept(listensock,(struct sockaddr)client,len);if (sock 0) // accept失败并不会终止进程, 只要获取下一个连接{cerr accept error endl;continue;}// 提取client信息 — debugstring clientipinet_ntoa(client.sin_addr); // 把4字节对应的IP转化成字符串风格uint16_t clientportntohs(client.sinport); // 网络序列转主机序列// 5. 获取新连接成功后, 开始进行业务处理cout获取新连接成功: sock from listensock, clientip - clientportendl;// v2: 多进程版本// 子进程对外提供服务, 父进程只进行acceptpidt idfork();if(id0){close(sock);continue;}else if(id0) // child, 父进程的fd会被子进程继承吗? 会; 父子会用同一张文件描述符表吗?不会, 子进程会拷贝继承父进程的fd table{// 建议关闭掉不需要的fdclose(listensock);if(fork()0) exit(0); // 就这一行代码// 子进程已经退了(则下面的wait立马返回, 回收子进程资源), 孙子进程在运行(无父进程, 变成孤儿进程, 被系统领养),提供服务// 孙子进程的回收工作由系统来承担service(sock,clientip,clientport);exit(0);}// 父进程, 一定要关闭不需要的fd(否则会导致父进程的文件描述符变少, 即父进程文件描述符资源的浪费[文件描述符泄露])close(sock);// 不等待子进程, 会导致子进程僵尸之后无法回收, 近而导致内存泄漏pid_t retwaitpid(id,nullptr,0); // 父进程默认是阻塞的, waitpid(id,nullptr,WNOHANG);不推荐if(retid)cout wait child id success endl;}}// 流式 - 利用read和writevoid service(int sock, const stringclientip,const uint16_t clientport){string whoclientip - to_string(clientport);char buffer[1024];while(true){ssizet sread(sock,buffer,sizeof(buffer)-1);if(s0){buffer[s]0;string resfunc(buffer); // 进行回调coutwho resendl;// 把收到的消息返回(写给客户端)write(sock,res.c_str(),res.size());}else if(s0){// 对方将连接关闭了close(sock);cout who quit, me tooendl;break;}else{close(sock);cerrread error: strerror(errno)endl;break;}}}~TcpServer(){}private:uint16t port;int listensock;bool quit; // 标志服务器是否运行字段funct func;}; }tcpServer.cc #includetcpServer.hpp using namespace ns_server;// ./tcp_server port// 使用手册 static void usage(string proc) {coutusage:\n\tproc port\nendl; }string echo(const stringmessage) {return message; }int main(int argc,char*argv[]) {if(argc!2){usage(argv[0]);exit(USAGE_ERR);}uint16_t portatoi(argv[1]);unique_ptrTcpServer tsvr(new TcpServer(echo,port));tsvr-InitServer();tsvr-Start();return 0; }tcpClient.cc #includeiostream #includesys/types.h #includesys/socket.h #includenetinet/in.h #includearpa/inet.h #includecstring #includeunistd.h #includememory #includefunctional #includeerr.hpp using namespace std;static void usage(string proc) {coutusage:\n\tproc serverip serverport\n endl; }// ./tcp_client serverip serverport int main(int argc,charargv[]) {// 准备工作if(argc!3){usage(argv[0]);exit(USAGE_ERR);}string serveripargv[1];uint16_t serverportatoi(argv[2]);// 1.创建套接字int socksocket(AF_INET,SOCK_STREAM,0);if (sock 0){cerr create socket error: strerror(errno) endl;exit(SOCKET_ERR);}// (2) 客户端要不要bind呢? 要// 要不要自己bind呢? 不要, 因为client要让OS自动给用户进行bind// (3) 要不要listen?不要, 客户端连别人, 永远都是别人listen; 要不要accept?不要, 服务器来连接// 2. connect 客户端向服务器发起连接请求struct sockaddr_in server;memset(server,0,sizeof(server));server.sin_porthtons(serverport);server.sin_familyAF_INET;// server.sin_addr.s_addrINADDR_ANY; 绝对不是inet_aton(serverip.c_str(),(server.sin_addr)); // 字符串风格ip转成点分十进制int cnt5;while(connect(sock,(struct sockaddr)server,sizeof(server))!0) // 连接失败{sleep(1);cout正在给你重连, 重连次数还有: cnt–endl;if(cnt0)break;}if(cnt0){cerr连接失败endl;exit(CONNECT_ERR);}char buffer[1024];// 3. 连接成功while(true){string line;coutEnter ;getline(cin,line);write(sock,line.c_str(),line.size());ssize_t s read(sock, buffer, sizeof(buffer)-1);if (s 0){buffer[s] 0;coutserver echo bufferendl;}else if (s 0){cerr server quit endl;break;}else{cerr read error: strerror(errno) endl;break;}}close(sock);return 0; }运行结果: (3) v3_多线程版本 频繁的创建进程会给OS带来巨大的负担并且创建线程的成本比创建线程高得多。因此在实现多执行流的服务器时最好采用多线程进行实现。 主线程创建出新线程后也是需要等待新线程退出的否则也会造成类似于僵尸进程这样的问题。但对于线程来说如果不想让主线程等待新线程退出直接线程分离即可当这个线程退出时系统会自动回收该线程所对应的资源。 各个线程共享是同一张文件描述符表也就是说服务进程主线程调用accept函数获取到一个文件描述符后其他创建的新线程是能够直接访问这个文件描述符的。 所以不能关闭不要的套接字文件描述符该文件描述符的关闭操作应该又新线程来执行。因为是新线程为客户端提供服务的只有当新线程为客户端提供的服务结束后才能将该文件描述符关闭。 tcpServer.hpp #includeiostream #includesys/types.h #includesys/socket.h #includenetinet/in.h #includearpa/inet.h #includecstring #includeunistd.h #includememory #includefunctional #includesys/wait.h #includepthread.h #includeerr.hpp using namespace std;namespace ns_server {static const uint16_t defaultport8081;static int backlog32;using func_tfunctionstring(const string); // 回调函数,一种处理逻辑class TcpServer;class ThreadData{public:ThreadData(int fd, const stringip,const uint16_tport,TcpServer*ts):sock(fd),clientip(ip),clientport(port),current(ts){}public:int sock;string clientip;uint16_t clientport;TcpServercurrent;};class TcpServer{public:TcpServer(func_t func, uint16t portdefaultport):func(func),port(port),quit(true){}void InitServer(){// 1. 创建socket文件listensock_socket(AF_INET,SOCK_STREAM,0);if(listensock_0){cerrcreate socket errorendl;exit(SOCKET_ERR);}// 2. bindstruct sockaddr_in local;memset(local,0,sizeof(local));local.sinporthtons(port);local.sin_familyAF_INET;local.sin_addr.s_addrINADDRANY;int nbind(listensock,(struct sockaddr)local,sizeof(local));if(n0){cerrbind socket errorendl;exit(BINDERR);}// 3. 监听int mlisten(listensock,backlog);if(m0){cerrlisten socket errorendl;exit(LISTEN_ERR);}}void Start(){// signal(SIGCHLD,SIG_IGN); // ok, 最推荐// signal(SIGCHLD,handler); // 回收子进程, 不太推荐quitfalse;while(!quit){struct sockaddr_in client;socklent lensizeof(client);// 4. 获取连接, acceptint sockaccept(listensock,(struct sockaddr*)client,len);if (sock 0) // accept失败并不会终止进程, 只要获取下一个连接{cerr accept error endl;continue;}// 提取client信息 — debugstring clientipinet_ntoa(client.sin_addr); // 把4字节对应的IP转化成字符串风格uint16_t clientportntohs(client.sinport); // 网络序列转主机序列// 5. 获取新连接成功后, 开始进行业务处理cout获取新连接成功: sock from listensock, clientip - clientportendl;// v3: 多线程版本 — 原生多线程// 1. 要不要关闭不要的socket? 绝对不能,一个进程的文件描述符表共享, 关了影响其他线程// 2. 要不要回收线程?要;如何回收?会不会阻塞pthread_t tid;ThreadData*tdnew ThreadData(sock,clientip,clientport,this); // 要开出一块独立的空间pthread_create(tid,nullptr,threadRoutine,td);}}static void*threadRoutine(void*args){pthread_detach(pthread_self());ThreadDatatdstatic_castThreadData(args);td-current-service(td-sock,td-clientip,td-clientport);delete td;}// 流式 - 利用read和writevoid service(int sock, const stringclientip,const uint16_t clientport){string whoclientip - to_string(clientport);char buffer[1024];while(true){ssizet sread(sock,buffer,sizeof(buffer)-1);if(s0){buffer[s]0;string resfunc(buffer); // 进行回调coutwho resendl;// 把收到的消息返回(写给客户端)write(sock,res.c_str(),res.size());}else if(s0){// 对方将连接关闭了close(sock);cout who quit, me tooendl;break;}else{close(sock);cerrread error: strerror(errno)endl;break;}}}~TcpServer(){}private:uint16t port;int listensock;bool quit; // 标志服务器是否运行字段funct func;}; }tcpServer.cc #includetcpServer.hpp using namespace ns_server;// ./tcp_server port// 使用手册 static void usage(string proc) {coutusage:\n\tproc port\nendl; }string echo(const stringmessage) {return message; }int main(int argc,char*argv[]) {if(argc!2){usage(argv[0]);exit(USAGE_ERR);}uint16_t portatoi(argv[1]);unique_ptrTcpServer tsvr(new TcpServer(echo,port));tsvr-InitServer();tsvr-Start();return 0; }tcpClient.cc #includeiostream #includesys/types.h #includesys/socket.h #includenetinet/in.h #includearpa/inet.h #includecstring #includeunistd.h #includememory #includefunctional #includeerr.hpp using namespace std;static void usage(string proc) {coutusage:\n\tproc serverip serverport\n endl; }// ./tcp_client serverip serverport int main(int argc,charargv[]) {// 准备工作if(argc!3){usage(argv[0]);exit(USAGE_ERR);}string serveripargv[1];uint16_t serverportatoi(argv[2]);// 1.创建套接字int socksocket(AF_INET,SOCK_STREAM,0);if (sock 0){cerr create socket error: strerror(errno) endl;exit(SOCKET_ERR);}// (2) 客户端要不要bind呢? 要// 要不要自己bind呢? 不要, 因为client要让OS自动给用户进行bind// (3) 要不要listen?不要, 客户端连别人, 永远都是别人listen; 要不要accept?不要, 服务器来连接// 2. connect 客户端向服务器发起连接请求struct sockaddr_in server;memset(server,0,sizeof(server));server.sin_porthtons(serverport);server.sin_familyAF_INET;// server.sin_addr.s_addrINADDR_ANY; 绝对不是inet_aton(serverip.c_str(),(server.sin_addr)); // 字符串风格ip转成点分十进制int cnt5;while(connect(sock,(struct sockaddr)server,sizeof(server))!0) // 连接失败{sleep(1);cout正在给你重连, 重连次数还有: cnt–endl;if(cnt0)break;}if(cnt0){cerr连接失败endl;exit(CONNECT_ERR);}char buffer[1024];// 3. 连接成功while(true){string line;coutEnter ;getline(cin,line);write(sock,line.c_str(),line.size());ssize_t s read(sock, buffer, sizeof(buffer)-1);if (s 0){buffer[s] 0;coutserver echo bufferendl;}else if (s 0){cerr server quit endl;break;}else{cerr read error: strerror(errno) endl;break;}}close(sock);return 0; }运行结果: (4) v4_线程池版本 多线程版的问题 每当有新连接到来时服务端的主线程都会为该客户端创建提供服务的新线程当服务结束时就会将新线程销毁这样做既麻烦又效率低下每当有新连接到来才开始创建提供服务的新线程若有大量的客户端请求此时服务端要为每一个客户端创建对应的服务线程。计算机中的线程越多CPU的压力越大 线程池 在服务端预先创建一批线程当有客户端请求连接时就让这些线程为客户端提供服务此时客户端一来就有线程为其提供服务而不是当客户端来了才创建对应的服务线程(减少了频繁创建线程的开销)当某个线程为客户端提供完服务后不要让该线程退出而是让该线程继续为下一个客户端提供服务如果当前没有客户端连接请求则可以让该线程先进入休眠状态当有客户端连接到来时再将该线程唤醒。服务端创建的这一批线程的数量不能太多此时CPU的压力也就不会太大 task.hpp #pragma once #includeiostream #includestring #includeunistd.h #includestring.h #includefunctional using namespace std;using cb_tfunctionvoid(int sock, const string,const uint16_t);class Task { public:Task(){}Task(int sock, const string ip,const uint16_tport,cb_t cb):_sock(sock),_ip(ip),_port(port),_cb(cb){}void operator()(){_cb(_sock,_ip,_port);}~Task(){}private:int _sock;string _ip;uint16_t _port;cb_t _cb; };LockGuard.hpp #includeiostream #includepthread.h using namespace std;class Mutex //自己不维护锁,由外部传入 { public:Mutex(pthread_mutex_t* mutex):_pmutex(mutex){}void lock(){pthread_mutex_lock(_pmutex);}void unlock(){pthread_mutex_unlock(_pmutex);}~Mutex(){}private:pthread_mutex_t* _pmutex; //锁的指针 };class LockGuard //自己不维护锁,由外部传入 { public:LockGuard(pthread_mutex_t* mutex):_mutex(mutex){_mutex.lock();}~LockGuard(){_mutex.unlock();}private:Mutex _mutex; //锁的指针 };thread.hpp #includeiostream #includestring using namespace std;class Thread { public:typedef enum{NEW0,RUNNING,EXITED}ThreadStatus;typedef void (func_t)(void); //函数指针, 参数是void*Thread(int num, func_t func, void*args):_tid(0),_status(NEW),_func(func),_args(args){char name[128];snprintf(name,sizeof(name),thread-%d,num);_namename;}int status() {return _status;}string threadname() {return _name;}pthread_t thread_id(){if(_statusRUNNING)return _tid;elsereturn 0;}// runHelper是不是类的成员函数, 而类的成员函数, 具有默认参数this, 需要static// void*runHelper(Thread*this, void*args) , 而pthread_create要求传的参数必须是: void*的, 即参数不匹配// 但是static会有新的问题: static成员函数, 无法直接访问类属性和其他成员函数static void*runHelper(void*args){Threadts(Thread)args; //就拿到了当前对象// _func(_args);(ts)();}//仿函数void operator()(){_func(_args);}void run(){int npthread_create(_tid,nullptr,runHelper,this); //this: 是当前线程对象Threadif(n!0) exit(-1);_statusRUNNING;}void join(){int npthread_join(_tid,nullptr);if(n!0){cerr main thread join thread _name error endl;}_statusEXITED;}~Thread(){}private:pthread_t _tid;string _name;func_t _func; //线程未来要执行的回调void_args; //调用回调函数时的参数ThreadStatus _status; };threadPool_v4.hpp #includeiostream #includememory #includevector #includequeue #includeunistd.h #includethread.hpp #includelockGuard.hpp using namespace std;const static int N5;templateclass T class threadPool { public:pthread_mutex_t* getlock(){return _lock;}void threadWait(){pthread_cond_wait(_cond,_lock);}void threadWakeup(){pthread_cond_signal(_cond); // 唤醒在条件变量下等待的线程}bool isEmpty(){return _tasks.empty();}T popTask(){T t_tasks.front();_tasks.pop();return t;}static void threadRoutine(void*args) {threadPoolTtpstatic_castthreadPoolT(args);while(true){// 1. 检测有没有任务 — 本质是看队列是否为空// — 本质就是在访问共享资源 — 必定加锁// 2. 有: 处理// 3. 无: 等待// 细节: 必定加锁T t;{LockGuard lockguard(tp-getlock());while (tp-isEmpty()){// 等待, 在条件变量下等待tp-threadWait();}t tp-popTask(); // 把任务从公共区域拿到私有区域}// for test// 处理任务应不应该在临界区中处理, 不应该, 这是线程自己私有的事情t(); }}static threadPoolT * getinstance(){if (instance nullptr) // 为什么要这样? 提高效率, 减少加锁的次数{LockGuard lockguard(instance_lock);if (instance nullptr){cout线程池单例形成endl;instance new threadPoolT();instance-init();instance-start();}}return instance;}void init(){for(int i0;i_num;i){_threads.push_back(Thread(i,threadRoutine,this));couti thread runningendl;}}void check(){for(autot:_threads){coutt.threadname() running…endl;}}void start(){for(autot:_threads){t.run();}}void pushTask(const Tt){LockGuard lockguard(_lock);_tasks.push(t);threadWakeup();}~threadPool(){for(autot:_threads){t.join();}pthread_mutex_destroy(_lock);pthread_cond_destroy(_cond);}private:threadPool(int numN):_num(num){pthread_mutex_init(_lock,nullptr);pthread_cond_init(_cond,nullptr);}threadPool(const threadPoolTtp)delete;void operator(const threadPoolTtp)delete;private:vectorThread _threads; int _num; queueT _tasks; pthread_mutex_t _lock;pthread_cond_t _cond;static threadPoolT*instance;static pthread_mutex_t instance_lock; };templateclass T threadPoolT * threadPoolT::instancenullptr;templateclass T pthread_mutex_t threadPoolT::instance_lockPTHREAD_MUTEX_INITIALIZER;tcpServer.hpp #includeiostream #includesys/types.h #includesys/socket.h #includenetinet/in.h #includearpa/inet.h #includecstring #includeunistd.h #includememory #includefunctional #includesys/wait.h #includepthread.h #includeerr.hpp #includethreadPool_v4.hpp #includetask.hpp using namespace std;namespace ns_server {static const uint16_t defaultport8081;static int backlog32;using func_tfunctionstring(const string); // 回调函数,一种处理逻辑class TcpServer;class ThreadData{public:ThreadData(int fd, const stringip,const uint16_tport,TcpServer*ts):sock(fd),clientip(ip),clientport(port),current(ts){}public:int sock;string clientip;uint16_t clientport;TcpServercurrent;};class TcpServer{public:TcpServer(func_t func, uint16t portdefaultport):func(func),port(port),quit(true){}void InitServer(){// 1. 创建socket文件listensock_socket(AF_INET,SOCK_STREAM,0);if(listensock_0){cerrcreate socket errorendl;exit(SOCKET_ERR);}// 2. bindstruct sockaddr_in local;memset(local,0,sizeof(local));local.sinporthtons(port);local.sin_familyAF_INET;local.sin_addr.s_addrINADDRANY;int nbind(listensock,(struct sockaddr)local,sizeof(local));if(n0){cerrbind socket errorendl;exit(BINDERR);}// 3. 监听int mlisten(listensock,backlog);if(m0){cerrlisten socket errorendl;exit(LISTEN_ERR);}}void Start(){quitfalse;while(!quit){struct sockaddr_in client;socklent lensizeof(client);// 4. 获取连接, acceptint sockaccept(listensock,(struct sockaddr*)client,len);if (sock 0) // accept失败并不会终止进程, 只要获取下一个连接{cerr accept error endl;continue;}// 提取client信息 — debugstring clientipinet_ntoa(client.sin_addr); // 把4字节对应的IP转化成字符串风格uint16_t clientportntohs(client.sinport); // 网络序列转主机序列// 5. 获取新连接成功后, 开始进行业务处理cout获取新连接成功: sock from listensock, clientip - clientportendl;// v4: 线程池版本 // 一旦用户来了你才创建线程, 线程池吗// 使用线程池的时候, 一定是有限的线程个数, 一定要处理短任务Task t(sock,clientip,clientport, bind(TcpServer::service, this,placeholders::_1,placeholders::_2,placeholders::_3));threadPoolTask::getinstance()-pushTask(t);}}static void*threadRoutine(void*args){pthread_detach(pthread_self());ThreadDatatdstatic_castThreadData(args);td-current-service(td-sock,td-clientip,td-clientport);delete td;}// 流式 - 利用read和writevoid service(int sock, const stringclientip,const uint16_t clientport){string whoclientip - to_string(clientport);char buffer[1024];ssizet s read(sock, buffer, sizeof(buffer) - 1);if (s 0){buffer[s] 0;string res func(buffer); // 进行回调cout who res endl;// 把收到的消息返回(写给客户端)write(sock, res.c_str(), res.size());}else if (s 0){cout who quit, me too endl;}else{close(sock);cerr read error: strerror(errno) endl;}close(sock);}~TcpServer(){}private:uint16t port;int listensock;bool quit; // 标志服务器是否运行字段funct func;}; }tcpServer.cc #includetcpServer.hpp using namespace ns_server;// ./tcp_server port// 使用手册 static void usage(string proc) {coutusage:\n\tproc port\nendl; }string echo(const stringmessage) {return message; }int main(int argc,char*argv[]) {if(argc!2){usage(argv[0]);exit(USAGE_ERR);}uint16_t portatoi(argv[1]);unique_ptrTcpServer tsvr(new TcpServer(echo,port));tsvr-InitServer();tsvr-Start();return 0; }tcpClient.cc #includeiostream #includesys/types.h #includesys/socket.h #includenetinet/in.h #includearpa/inet.h #includecstring #includeunistd.h #includememory #includefunctional #includeerr.hpp using namespace std;static void usage(string proc) {coutusage:\n\tproc serverip serverport\n endl; }// ./tcp_client serverip serverport int main(int argc,charargv[]) {// 准备工作if(argc!3){usage(argv[0]);exit(USAGE_ERR);}string serveripargv[1];uint16_t serverportatoi(argv[2]);// 1.创建套接字int socksocket(AF_INET,SOCK_STREAM,0);if (sock 0){cerr create socket error: strerror(errno) endl;exit(SOCKET_ERR);}// (2) 客户端要不要bind呢? 要// 要不要自己bind呢? 不要, 因为client要让OS自动给用户进行bind// (3) 要不要listen?不要, 客户端连别人, 永远都是别人listen; 要不要accept?不要, 服务器来连接// 2. connect 客户端向服务器发起连接请求struct sockaddr_in server;memset(server,0,sizeof(server));server.sin_porthtons(serverport);server.sin_familyAF_INET;// server.sin_addr.s_addrINADDR_ANY; 绝对不是inet_aton(serverip.c_str(),(server.sin_addr)); // 字符串风格ip转成点分十进制int cnt5;while(connect(sock,(struct sockaddr)server,sizeof(server))!0) // 连接失败{sleep(1);cout正在给你重连, 重连次数还有: cnt–endl;if(cnt0)break;}if(cnt0){cerr连接失败endl;exit(CONNECT_ERR);}char buffer[1024];// 3. 连接成功while(true){string line;coutEnter ;getline(cin,line);write(sock,line.c_str(),line.size());ssize_t s read(sock, buffer, sizeof(buffer)-1);if (s 0){buffer[s] 0;coutserver echo bufferendl;}else if (s 0){cerr server quit endl;break;}else{cerr read error: strerror(errno) endl;break;}}close(sock);return 0; }运行结果: ip serverport\n endl; } // ./tcp_client serverip serverport int main(int argc,charargv[]) { // 准备工作 if(argc!3) { usage(argv[0]); exit(USAGE_ERR); } string serveripargv[1]; uint16_t serverportatoi(argv[2]);// 1.创建套接字 int socksocket(AF_INET,SOCK_STREAM,0); if (sock 0) {cerr create socket error: strerror(errno) endl;exit(SOCKET_ERR); }// (2) 客户端要不要bind呢? 要 // 要不要自己bind呢? 不要, 因为client要让OS自动给用户进行bind // (3) 要不要listen?不要, 客户端连别人, 永远都是别人listen; 要不要accept?不要, 服务器来连接// 2. connect 客户端向服务器发起连接请求 struct sockaddr_in server; memset(server,0,sizeof(server)); server.sin_porthtons(serverport); server.sin_familyAF_INET; // server.sin_addr.s_addrINADDR_ANY; 绝对不是 inet_aton(serverip.c_str(),(server.sin_addr)); // 字符串风格ip转成点分十进制int cnt5;while(connect(sock,(struct sockaddr)server,sizeof(server))!0) // 连接失败 {sleep(1);cout正在给你重连, 重连次数还有: cnt–endl;if(cnt0)break; } if(cnt0) {cerr连接失败endl;exit(CONNECT_ERR); }char buffer[1024]; // 3. 连接成功 while(true) {string line;coutEnter ;getline(cin,line);write(sock,line.c_str(),line.size());ssize_t s read(sock, buffer, sizeof(buffer)-1);if (s 0){buffer[s] 0;coutserver echo bufferendl;}else if (s 0){cerr server quit endl;break;}else{cerr read error: strerror(errno) endl;break;} }close(sock); return 0;} 运行结果:
- 上一篇: 网站推广的工作内容网站有备案是正规的吗
- 下一篇: 网站推广的看法广告公司视频制作
相关文章
-
网站推广的工作内容网站有备案是正规的吗
网站推广的工作内容网站有备案是正规的吗
- 技术栈
- 2026年03月21日
-
网站推广的方式包括哪些吴江盛泽建设局网站
网站推广的方式包括哪些吴江盛泽建设局网站
- 技术栈
- 2026年03月21日
-
网站推广的定义如何选择做pc端网站
网站推广的定义如何选择做pc端网站
- 技术栈
- 2026年03月21日
-
网站推广的看法广告公司视频制作
网站推广的看法广告公司视频制作
- 技术栈
- 2026年03月21日
-
网站推广的六种方式做神马网站快
网站推广的六种方式做神马网站快
- 技术栈
- 2026年03月21日
-
网站推广的平台排名天津网站建设排名
网站推广的平台排名天津网站建设排名
- 技术栈
- 2026年03月21日
