当前位置:   article > 正文

【网络编程】实现UDP/TCP客户端、服务器_tcp udp server

tcp udp server


需要云服务器等云产品来学习Linux的同学可以移步/-->腾讯云<--/-->阿里云<--/-->华为云<--/官网,轻量型云服务器低至112元/年,新用户首次下单享超低折扣。


 目录

一、UDP

1、Linux客户端、服务器

1.1udpServer.hpp

1.2udpServer.cc

1.3udpClient.hpp

1.4udpClient.cc

1.5onlineUser.hpp

2、Windows客户端

二、TCP

1、单进程版的TCP客户端、服务器

1.1tcpServer.hpp

1.2tcpServer.cc

1.3tcpClient.hpp

1.4tcpClient.cc

1.5log.hpp

2、多进程版的TCP客户端、服务器

3、多线程版的TCP客户端、服务器

4、线程池版的TCP客户端、服务器

4.1tcpServer.hpp

4.2ThreadPool.hpp 

4.3Task.hpp

5、守护进程+多线程版的TCP客户端、服务器

5.1daemon.hpp

5.2tcpServer.cc


UDP/TCP客户端、服务器代码可参考本人gitee

UDP/TCP套接字的创建流程可参考此处

一、UDP

1、Linux客户端、服务器

1.1udpServer.hpp

  1. #pragma once
  2. #include <iostream>
  3. #include <string>
  4. #include <sys/types.h>
  5. #include <sys/socket.h>
  6. #include <cerrno>
  7. #include <cstring>
  8. #include <cstdlib>
  9. #include <arpa/inet.h>
  10. #include <strings.h>
  11. #include <functional>
  12. namespace Server
  13. {
  14. const static string defaultIp="0.0.0.0";//缺省的IP
  15. const static int gnum=1024;
  16. typedef function<void(int,string,uint16_t,string)> func_t;
  17. enum
  18. {
  19. USAGE_ERR=1,
  20. SOCKET_ERR,
  21. BIND_ERR,
  22. OPEN_ERR,
  23. };
  24. class udpServer
  25. {
  26. public:
  27. udpServer(const func_t& callback,const uint16_t& port,const string& ip=defaultIp)
  28. :_callback(callback)//udpServer.cc传入的对客户端数据处理的函数
  29. ,_port(port)
  30. ,_ip(ip)
  31. ,_sockfd(-1)
  32. {}
  33. void initServer()
  34. {
  35. //1、创建socket
  36. _sockfd=socket(AF_INET,SOCK_DGRAM,0);//网络通信+数据报
  37. if(-1==_sockfd)
  38. {
  39. cout<<"socket error"<<errno<<":"<<strerror(errno)<<endl;
  40. exit(SOCKET_ERR);
  41. }
  42. cout<<"socket success"<<":"<<_sockfd<<endl;
  43. //2、绑定IP和端口号
  44. struct sockaddr_in local;
  45. bzero(&local,sizeof(local));//将一段内存初始化为全0
  46. local.sin_family=AF_INET;//协议族设置为网络通信
  47. local.sin_port=htons(_port);//设置端口号,需要转为大端,主机转网络
  48. local.sin_addr.s_addr=inet_addr(_ip.c_str());//将IP字符串转uint32_t的同时转为网络字节序
  49. //local.sin_addr.s_addr=htonl(INADDR_ANY);//INADDR_ANY就是0,表明任何IP都可以访问这个服务器的_port端口
  50. int n=bind(_sockfd,(struct sockaddr*)&local,sizeof(local));
  51. if(-1==n)
  52. {
  53. cout<<"bind error"<<errno<<":"<<strerror(errno)<<endl;
  54. exit(BIND_ERR);
  55. }
  56. }
  57. void start()
  58. {
  59. char buffer[gnum];
  60. while(1)
  61. {
  62. //循环读取数据
  63. struct sockaddr_in local;//输出型参数
  64. socklen_t len=sizeof(local);//必填
  65. size_t s=recvfrom(_sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&local,&len);//阻塞式读取
  66. //这里需要关心1、数据是什么2、数据是谁发的
  67. if(s>0)
  68. {
  69. buffer[s]=0;//加上'\0'
  70. //1、这是从网络读出来的IP,需要由网络字节序转主机字节序2、整数转点分十进制IP,用inet_ntoa进行转换
  71. string clientIp=inet_ntoa(local.sin_addr);//将32位IPv4地址(in_addr结构体)转换成点分十进制字符串形式的IP地址
  72. uint16_t clientPort=ntohs(local.sin_port);//一样需要转换字节序
  73. string message=buffer;
  74. cout<<clientIp<<"["<<clientPort<<"]#"<<message<<endl;
  75. //对数据进行处理
  76. _callback(_sockfd,clientIp,clientPort,message);
  77. }
  78. }
  79. }
  80. ~udpServer()
  81. {
  82. }
  83. private:
  84. uint16_t _port;//端口号
  85. string _ip;//IP地址(服务器不建议固定的绑定一个IP地址,因为服务器需要接收所有的IP)
  86. int _sockfd;//套接字文件描述符
  87. func_t _callback;//回调函数
  88. };
  89. }

1.2udpServer.cc

  1. #include <memory>
  2. #include <unordered_map>
  3. #include <fstream>
  4. #include <signal.h>
  5. using namespace std;
  6. #include "udpServer.hpp"
  7. #include "onlineUser.hpp"
  8. using namespace Server;
  9. // const std::string dictTxt="./dict.txt";
  10. // unordered_map<string,string> dict;//字典
  11. // std::string key,value;
  12. static void Usage(string proc)
  13. {
  14. cout<<"Usage:\n\t"<<proc<<"local_ip local_port\n\n";
  15. }
  16. // static bool cutString(const string& target,string* key,string* value,const string& sep)//字符串截取
  17. // {
  18. // //string sep=":";
  19. // auto pos=target.find(sep,0);
  20. // if(pos==string::npos)
  21. // {
  22. // return false;
  23. // }
  24. // *key=target.substr(0,pos);
  25. // *value=target.substr(pos+sep.size());
  26. // return true;
  27. // }
  28. // static void initDict()//文件操作
  29. // {
  30. // ifstream in(dictTxt,std::ios_base::binary);
  31. // if(!in.is_open())//如果文件打开失败
  32. // {
  33. // cerr<<"open file"<<dictTxt<<"error"<<endl;
  34. // exit(OPEN_ERR);
  35. // }
  36. // string line;
  37. // while(getline(in,line))
  38. // {
  39. // if(cutString(line,&key,&value,":"))//如果截取成功
  40. // {
  41. // dict.insert(make_pair(key,value));//dict.insert(key,value);
  42. // }
  43. // else //截取失败
  44. // {
  45. // //...
  46. // }
  47. // }
  48. // in.close();
  49. // cout<<"load dict success"<<endl;
  50. // }
  51. // static void debugPrint()//测试打印函数
  52. // {
  53. // for(auto& dt:dict)
  54. // {
  55. // cout<<dt.first<<"/"<<dt.second<<endl;
  56. // }
  57. // }
  58. // //客户端单词翻译代码
  59. // void handMessage(int sockfd,string clientIp,uint16_t clientPort,string message)
  60. // {
  61. // //对客户端的信息进行特定的业务处理,实现了server通信和业务的解耦
  62. // string response_message;//将查找的字符串保存至此处
  63. // unordered_map<string,string>::iterator iter=dict.find(message);
  64. // if(iter==dict.end())
  65. // {
  66. // response_message="unknow";
  67. // }
  68. // else
  69. // response_message=iter->second;
  70. // //服务端向客户端回发数据
  71. // struct sockaddr_in client;
  72. // bzero(&client,sizeof(client));
  73. // client.sin_family=AF_INET;
  74. // client.sin_addr.s_addr=inet_addr(clientIp.c_str());
  75. // client.sin_port=htons(clientPort);
  76. // sendto(sockfd,response_message.c_str(),response_message.size(),0,(struct sockaddr*)&client,sizeof(client));
  77. // }
  78. // //解析客户端上传的命令
  79. // void execCommand(int sockfd,string clientIp,uint16_t clientPort,string cmd)
  80. // {
  81. // //对客户端的信息进行特定的业务处理,实现了server通信和业务的解耦
  82. // auto end=cmd.find("rm");
  83. // if(end!=string::npos)
  84. // {
  85. // cerr<<clientIp<<":"<<clientPort<<"非法操作"<<cmd<<endl;
  86. // return;
  87. // }
  88. // string response_message;//将客户端上传的指令保存至此处
  89. // FILE* fp=popen(cmd.c_str(),"r");
  90. // if(fp==nullptr)
  91. // {
  92. // response_message=cmd+" exec failed";
  93. // }
  94. // char line[1024];
  95. // while(fgets(line,sizeof(line),fp))
  96. // {
  97. // response_message+=line;//读出客户端传入的指令
  98. // }
  99. // pclose(fp);
  100. // //服务端向客户端回发数据
  101. // struct sockaddr_in client;
  102. // bzero(&client,sizeof(client));
  103. // client.sin_family=AF_INET;
  104. // client.sin_addr.s_addr=inet_addr(clientIp.c_str());
  105. // client.sin_port=htons(clientPort);
  106. // sendto(sockfd,response_message.c_str(),response_message.size(),0,(struct sockaddr*)&client,sizeof(client));
  107. // }
  108. //聊天室
  109. OnlineUser olUser;
  110. void routeMessage(int sockfd,string clientIp,uint16_t clientPort,string message)
  111. {
  112. //上线就新增,下线就减掉
  113. if(message=="online")
  114. {
  115. olUser.addUser(clientIp,clientPort);
  116. }
  117. if(message=="offline")
  118. {
  119. olUser.delUser(clientIp,clientPort);
  120. }
  121. if(olUser.isOnline(clientIp,clientPort))
  122. {
  123. //广播消息
  124. olUser.broadcastMessage(sockfd,clientIp,clientPort,message);
  125. }
  126. else
  127. {
  128. //服务端向客户端回发数据
  129. string response_message="请先运行online";
  130. struct sockaddr_in client;
  131. bzero(&client,sizeof(client));
  132. client.sin_family=AF_INET;
  133. client.sin_addr.s_addr=inet_addr(clientIp.c_str());
  134. client.sin_port=htons(clientPort);
  135. sendto(sockfd,response_message.c_str(),response_message.size(),0,(struct sockaddr*)&client,sizeof(client));
  136. }
  137. }
  138. // void reload(int signo)//热加载回调函数
  139. // {
  140. // (void)signo;
  141. // initDict();
  142. // }
  143. int main(int argc,char* argv[])//./udpServer port
  144. {
  145. if(argc!=2)//判断外部传入的参数是否为3
  146. {
  147. Usage(argv[0]);
  148. exit(USAGE_ERR);
  149. }
  150. uint16_t port=atoi(argv[1]);//需要转uint16_t整型
  151. // signal(2,reload);//发送信号,实现文本的热加载
  152. // initDict();
  153. //std::unique_ptr<udpServer> usvr(new udpServer(handMessage,port));//在线翻译
  154. //std::unique_ptr<udpServer> usvr(new udpServer(execCommand,port));//指令解析
  155. std::unique_ptr<udpServer> usvr(new udpServer(routeMessage,port));//聊天室
  156. usvr->initServer();
  157. usvr->start();
  158. return 0;
  159. }

1.3udpClient.hpp

  1. #pragma once
  2. #include <iostream>
  3. #include <string>
  4. #include <sys/types.h>
  5. #include <sys/socket.h>
  6. #include <pthread.h>
  7. #include <cerrno>
  8. #include <cstring>
  9. #include <cstdlib>
  10. #include <arpa/inet.h>
  11. #include <strings.h>
  12. namespace Client
  13. {
  14. using namespace std;
  15. class udpClient
  16. {
  17. public:
  18. udpClient(const string& serverIp,const uint16_t& serverPort)
  19. :_sockfd(-1)
  20. ,_serverPort(serverPort)
  21. ,_serverIp(serverIp)
  22. {}
  23. void initClient()
  24. {
  25. //创建socket
  26. _sockfd=socket(AF_INET,SOCK_DGRAM,0);
  27. if(-1==_sockfd)
  28. {
  29. cout<<"socket error"<<errno<<":"<<strerror(errno)<<endl;
  30. exit(2);
  31. }
  32. cout<<"socket syuccess"<<":"<<_sockfd<<endl;
  33. }
  34. static void* readMessage(void* args)//类内创建线程,有个this指针干扰
  35. {
  36. int sockfd=*static_cast<int*>(args);
  37. pthread_detach(pthread_self());
  38. while(1)
  39. {
  40. //接收服务端发送的数据
  41. char buffer[1024];
  42. struct sockaddr_in temp;
  43. socklen_t len=sizeof(temp);
  44. size_t s=recvfrom(sockfd,buffer,sizeof(buffer),0,(struct sockaddr*)&temp,&len);
  45. if(s>0)
  46. {
  47. buffer[s]=0;//字符串以'\0'结尾
  48. }
  49. cout<<buffer<<endl;
  50. }
  51. return nullptr;
  52. }
  53. void run()
  54. {
  55. pthread_create(&_reader,nullptr,readMessage,(void*)&_sockfd);
  56. struct sockaddr_in server;
  57. memset(&server,sizeof(server),0);//初始化为全0
  58. server.sin_family=AF_INET;
  59. server.sin_addr.s_addr=inet_addr(_serverIp.c_str());
  60. server.sin_port=htons(_serverPort);//主机转网络
  61. string message;
  62. char cmdline[1024];
  63. while(1)
  64. {
  65. //cerr<<"Please Enter#";
  66. // cin>>message;
  67. fprintf(stderr,"Enter#");
  68. fflush(stderr);
  69. fgets(cmdline,sizeof(cmdline),stdin);
  70. cmdline[strlen(cmdline)-1]=0;
  71. message=cmdline;
  72. //发送数据,sendto的时候,操作系统会帮我们自动绑定客户端端口+IP地址
  73. sendto(_sockfd,message.c_str(),message.size(),0,(struct sockaddr*)&server,sizeof(server));
  74. }
  75. }
  76. ~udpClient()
  77. {}
  78. private:
  79. int _sockfd;
  80. uint16_t _serverPort;
  81. string _serverIp;
  82. pthread_t _reader;//读线程
  83. };
  84. }

1.4udpClient.cc

  1. #include <memory>
  2. #include "udpClient.hpp"
  3. using namespace Client;
  4. static void Usage(string proc)
  5. {
  6. cout<<"Usage:\n\t"<<proc<<"server_ip server_port\n\n";
  7. }
  8. int main(int argc,char* argv[])//./udpClient server_ip server_port
  9. {
  10. if(argc!=3)
  11. {
  12. Usage(argv[0]);
  13. exit(1);
  14. }
  15. string serverIp=argv[1];
  16. uint16_t serverPort=atoi(argv[2]);
  17. unique_ptr<udpClient> ucli(new udpClient(serverIp,serverPort));
  18. ucli->initClient();
  19. ucli->run();
  20. return 0;
  21. }

1.5onlineUser.hpp

  1. #pragma once
  2. #include <iostream>
  3. #include <string>
  4. #include <unordered_map>
  5. #include <sys/types.h>
  6. #include <sys/socket.h>
  7. #include <cerrno>
  8. #include <cstring>
  9. #include <cstdlib>
  10. #include <arpa/inet.h>
  11. using namespace std;
  12. class User
  13. {
  14. public:
  15. User(const string& ip,const uint16_t& port)
  16. :_ip(ip)
  17. ,_port(port)
  18. {
  19. }
  20. ~User()
  21. {}
  22. string ip()
  23. {
  24. return _ip;
  25. }
  26. uint16_t port()
  27. {
  28. return _port;
  29. }
  30. private:
  31. string _ip;//用户IP
  32. uint16_t _port;//用户端口号
  33. };
  34. class OnlineUser
  35. {
  36. public:
  37. OnlineUser()
  38. {}
  39. ~OnlineUser()
  40. {}
  41. void addUser(const string& ip,const uint16_t& port)//新增用户
  42. {
  43. string id=ip+"-"+to_string(port);
  44. users.insert(make_pair(id,User(ip,port)));
  45. }
  46. void delUser(const string& ip,const uint16_t& port)//删除用户
  47. {
  48. string id=ip+"-"+to_string(port);
  49. users.erase(id);
  50. }
  51. bool isOnline(const string& ip,const uint16_t& port)//是否在线
  52. {
  53. string id=ip+"-"+to_string(port);
  54. return users.find(id)==users.end()?false:true;
  55. }
  56. void broadcastMessage(int sockfd,const string& ip,const uint16_t& port,const string& message)//给所有的user广播消息
  57. {
  58. for(auto& user:users)
  59. {
  60. //服务端向客户端回发数据
  61. struct sockaddr_in client;
  62. bzero(&client,sizeof(client));
  63. client.sin_family=AF_INET;
  64. client.sin_addr.s_addr=inet_addr(user.second.ip().c_str());
  65. client.sin_port=htons(user.second.port());
  66. string s=ip+"_"+to_string(port)+"# ";//id+"#"
  67. s+=message;
  68. sendto(sockfd,s.c_str(),s.size(),0,(struct sockaddr*)&client,sizeof(client));
  69. }
  70. }
  71. private:
  72. unordered_map<string,User> users;//string:id=ip+"-"+to_string(port);User:User类
  73. };

2、Windows客户端

        可先让上方Linux服务器先运行起来,再让Windows客户端连接上该服务端,实现网络通信。

  1. #define _WINSOCK_DEPRECATED_NO_WARNINGS
  2. #include <iostream>
  3. #include <WinSock2.h>
  4. #include <string>
  5. #include <cstring>
  6. #pragma comment(lib,"ws2_32.lib")
  7. using namespace std;
  8. uint16_t serverPort = 8080;
  9. string serverIp = "43.XXX.105.XX";//你的云服务器IP
  10. #define NUM 1024
  11. int main()
  12. {
  13. WSAData wsd;
  14. //启动Winsock
  15. if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
  16. {
  17. cout << "WSAStartUp Error = " << WSAGetLastError() << endl;
  18. return -1;
  19. }
  20. else
  21. {
  22. cout << "WSAStartup Success" << endl;
  23. }
  24. SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);//创建套接字
  25. if (sock == SOCKET_ERROR)
  26. {
  27. cout<<"socket Error = "<< WSAGetLastError() << endl;
  28. return -2;
  29. }
  30. else
  31. {
  32. cout << "socket Success" << endl;
  33. }
  34. struct sockaddr_in server;
  35. memset(&server, 0, sizeof(server));
  36. server.sin_addr.s_addr = inet_addr(serverIp.c_str());
  37. server.sin_family = AF_INET;
  38. server.sin_port = htons(serverPort);
  39. string line;
  40. char buffer[NUM];
  41. while (1)
  42. {
  43. //发送数据
  44. cout << "Please Enter#";
  45. getline(cin, line);
  46. int n = sendto(sock, line.c_str(), line.size(), 0, (struct sockaddr*)&server, sizeof(server));
  47. if (n < 0)
  48. {
  49. cerr << "sendto Error" << endl;
  50. break;
  51. }
  52. cout << "发送成功" << endl;
  53. //接收数据
  54. buffer[0] = 0;//C式清空数组
  55. struct sockaddr_in peer;
  56. int len = (int)sizeof(peer);
  57. n = recvfrom(sock, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)&peer, &len);
  58. if (n > 0)
  59. {
  60. buffer[n] = 0;
  61. cout << "server 返回的消息是" << buffer << endl;
  62. }
  63. else break;
  64. }
  65. closesocket(sock);//关闭套接字
  66. WSACleanup();
  67. return 0;
  68. }

二、TCP

1、单进程版的TCP客户端、服务器

        单线程会一直在ServerIO读取写入数据,为一个客户端服务,如果此时连接的客户端不止一个,其他客户端发送的信息将不会被显示。需要使用多线程或多进程解决。

1.1tcpServer.hpp

  1. #pragma once
  2. #include <iostream>
  3. #include <unistd.h>
  4. #include <sys/types.h>
  5. #include <sys/socket.h>
  6. #include <arpa/inet.h>
  7. #include <netinet/in.h>
  8. #include <string>
  9. #include <cstring>
  10. #include "log.hpp"
  11. namespace Server
  12. {
  13. enum
  14. {
  15. USAGE_ERR=1,
  16. SOCKET_ERR,
  17. BIND_ERR,
  18. LISTEN_ERR,
  19. };
  20. static const uint16_t gport=8080;//缺省的端口号
  21. static const int gbacklog=5;//最大连接数=5+1
  22. const static std::string defaultIp="0.0.0.0";//缺省的IP
  23. class TcpServer
  24. {
  25. public:
  26. TcpServer(const uint16_t& port=gport,const std::string& ip=defaultIp )
  27. :_listenSocket(-1)
  28. ,_port(port)
  29. ,_ip(ip)
  30. {
  31. }
  32. void InitServer()//初始化服务器
  33. {
  34. //1、创建sockrt套接字
  35. _listenSocket=socket(AF_INET,SOCK_STREAM,0);
  36. if(_listenSocket<0)
  37. {
  38. LogMessage(FATAL,"create socket error");
  39. exit(SOCKET_ERR);
  40. }
  41. LogMessage(NORMAL,"create socket success");
  42. //2、绑定端口号+ip地址
  43. struct sockaddr_in local;
  44. memset(&local,0,sizeof(local));
  45. local.sin_addr.s_addr=inet_addr(_ip.c_str());
  46. local.sin_family=AF_INET;
  47. local.sin_port=htons(_port);
  48. if(bind(_listenSocket,(struct sockaddr*)&local,sizeof(local))<0)
  49. {
  50. LogMessage(FATAL,"bind socket error");
  51. exit(BIND_ERR);
  52. }
  53. LogMessage(NORMAL,"bind socket success");
  54. //3、设置监听状态
  55. if(-1==listen(_listenSocket,gbacklog))
  56. {
  57. LogMessage(FATAL,"listen socket error");
  58. exit(LISTEN_ERR);
  59. }
  60. LogMessage(NORMAL,"listen socket success");
  61. }
  62. void Start()//启动服务器
  63. {
  64. while(1)
  65. {
  66. //4、服务器获取客户端连接请求
  67. struct sockaddr_in peer;//输出型参数,拿到客户端的信息
  68. socklen_t len=sizeof(peer);
  69. int sock=accept(_listenSocket,(struct sockaddr*)&peer,&len);
  70. if(-1==sock)
  71. {
  72. LogMessage(ERROR,"accept error,next");
  73. continue;
  74. }
  75. LogMessage(NORMAL,"accept a new link success");
  76. //5、使用accept的返回值sock进行通信,均为文件操作
  77. ServerIO(sock);
  78. close(sock);//必须关闭使用完毕的sock,否则文件描述符泄漏
  79. }
  80. }
  81. void ServerIO(int sock)
  82. {
  83. char buffer[1024];
  84. while(1)
  85. {
  86. //服务器读取客户端数据,通过套接字sock这个文件描述符读取数据
  87. ssize_t n=read(sock,buffer,sizeof(buffer)-1);
  88. if(n>0)
  89. {
  90. buffer[n]=0;
  91. std::cout<<"recv message:"<<buffer<<std::endl;
  92. std::string outBuffer=buffer;
  93. outBuffer+="[server echo]";
  94. //服务器将数据处理后发送回客户端
  95. write(sock,outBuffer.c_str(),outBuffer.size());
  96. }
  97. else if(0==n)//服务器read返回值为0,说明客户端关闭了
  98. {
  99. LogMessage(NORMAL,"client quit,server quit");
  100. break;
  101. }
  102. }
  103. }
  104. ~TcpServer()
  105. {}
  106. private:
  107. int _listenSocket;//监听客户端的连接请求,不用于数据通信
  108. uint16_t _port;//服务器端口号
  109. std::string _ip;//服务器ip地址
  110. };
  111. }

1.2tcpServer.cc

  1. #include "tcpServer.hpp"
  2. #include "memory"
  3. using namespace Server;
  4. static void Usage(std::string proc)
  5. {
  6. std::cout<<"Usage:\n\t"<<proc<<"serverPort\n\n";
  7. }
  8. //./tcpServer local_port
  9. int main(int argc,char* argv[])
  10. {
  11. if(argc!=2)//判断外部传入的参数是否为2
  12. {
  13. Usage(argv[0]);
  14. exit(USAGE_ERR);
  15. }
  16. uint16_t port=std::stoi(argv[1]);
  17. std::unique_ptr<TcpServer> tsvr(new TcpServer(port));
  18. tsvr->InitServer();
  19. tsvr->Start();
  20. return 0;
  21. }

1.3tcpClient.hpp

  1. #pragma once
  2. #include <iostream>
  3. #include <unistd.h>
  4. #include <sys/types.h>
  5. #include <sys/socket.h>
  6. #include <arpa/inet.h>
  7. #include <netinet/in.h>
  8. #include <string>
  9. #include <cstring>
  10. #define NUM 1024
  11. class TcpClient
  12. {
  13. public:
  14. TcpClient(const std::string& serverIp,const uint16_t& serverPort)
  15. :_serverIp(serverIp)
  16. ,_serverPort(serverPort)
  17. ,_sock(-1)
  18. {
  19. }
  20. void InitClient()
  21. {
  22. //1、创建套接字
  23. _sock=socket(AF_INET,SOCK_STREAM,0);
  24. if(_sock<0)
  25. {
  26. std::cerr<<"cerete socket err"<<std::endl;
  27. exit(2);
  28. }
  29. //2、客户端需要bind,但是客户端的绑定不需要我们自己写,操作系统会去绑定;(无需程序员bind)
  30. }
  31. void Start()
  32. {
  33. //3、客户端发起连接
  34. struct sockaddr_in server;
  35. memset(&server,0,sizeof(server));
  36. server.sin_addr.s_addr=inet_addr(_serverIp.c_str());
  37. server.sin_family=AF_INET;
  38. server.sin_port=htons(_serverPort);
  39. if(connect(_sock,(struct sockaddr*)&server,sizeof(server))<0)//连接失败
  40. {
  41. std::cerr<<"sock connect error"<<std::endl;
  42. }
  43. else//连接成功
  44. {
  45. //4、客户端发送/接收消息,文件操作
  46. std::string msg;
  47. while(1)
  48. {
  49. std::cout<<"Enter:";
  50. std::getline(std::cin,msg);
  51. write(_sock,msg.c_str(),msg.size());
  52. char buffer[NUM];
  53. int n=read(_sock,buffer,sizeof(buffer)-1);
  54. if(n>0)
  55. {
  56. buffer[n]=0;
  57. std::cout<<"Server 回显消息:"<<buffer<<std::endl;
  58. }
  59. else
  60. break;
  61. }
  62. }
  63. }
  64. ~TcpClient()
  65. {
  66. if(_sock>=0)
  67. {
  68. close(_sock);
  69. }
  70. }
  71. private:
  72. int _sock;//客户端套接字
  73. uint16_t _serverPort;//服务器端口号
  74. std::string _serverIp;//服务器ip
  75. };

1.4tcpClient.cc

  1. #include "tcpClient.hpp"
  2. #include <memory>
  3. static void Usage(std::string proc)
  4. {
  5. std::cout<<"Usage:\n\t"<<proc<<"local_ip local_port\n\n";
  6. }
  7. //./tcpClient serverIp serverPort
  8. int main(int argc,char* argv[])
  9. {
  10. if(argc!=3)
  11. {
  12. Usage(argv[0]);
  13. exit(1);
  14. }
  15. std::string serverIp=argv[1];
  16. uint16_t serverPort=std::stoi(argv[2]);
  17. std::unique_ptr<TcpClient> tcli(new TcpClient(serverIp,serverPort));
  18. tcli->InitClient();
  19. tcli->Start();
  20. return 0;
  21. }

1.5log.hpp

  1. #pragma once
  2. #include <iostream>
  3. #include <string>
  4. #define DEBUG 0
  5. #define NORMAL 1
  6. #define WARNING 2
  7. #define ERROR 3
  8. #define FATAL 4
  9. //日志功能
  10. void LogMessage(int level,const std::string& message)
  11. {
  12. //[日志等级][时间戳/时间][pid][message]
  13. std::cout<<message<<std::endl;
  14. }

2、多进程版的TCP客户端、服务器

        更换tcpServer.hpp即可,其他文件和单进程版一样。

  1. #pragma once
  2. #include <iostream>
  3. #include <unistd.h>
  4. #include <sys/types.h>
  5. #include <sys/wait.h>
  6. #include <sys/socket.h>
  7. #include <arpa/inet.h>
  8. #include <netinet/in.h>
  9. #include <string>
  10. #include <cstring>
  11. #include <cstdlib>
  12. #include "log.hpp"
  13. namespace Server
  14. {
  15. enum
  16. {
  17. USAGE_ERR=1,
  18. SOCKET_ERR,
  19. BIND_ERR,
  20. LISTEN_ERR,
  21. };
  22. static const uint16_t gport=8080;//缺省的端口号
  23. static const int gbacklog=5;//最大连接数=5+1
  24. const static std::string defaultIp="0.0.0.0";//缺省的IP
  25. class TcpServer
  26. {
  27. public:
  28. TcpServer(const uint16_t& port=gport,const std::string& ip=defaultIp )
  29. :_listenSocket(-1)
  30. ,_port(port)
  31. ,_ip(ip)
  32. {
  33. }
  34. void InitServer()//初始化服务器
  35. {
  36. //1、创建sockrt套接字
  37. _listenSocket=socket(AF_INET,SOCK_STREAM,0);
  38. if(_listenSocket<0)
  39. {
  40. LogMessage(FATAL,"create socket error");
  41. exit(SOCKET_ERR);
  42. }
  43. LogMessage(NORMAL,"create socket success");
  44. //2、绑定端口号+ip地址
  45. struct sockaddr_in local;
  46. memset(&local,0,sizeof(local));
  47. local.sin_addr.s_addr=inet_addr(_ip.c_str());
  48. local.sin_family=AF_INET;
  49. local.sin_port=htons(_port);
  50. if(bind(_listenSocket,(struct sockaddr*)&local,sizeof(local))<0)
  51. {
  52. LogMessage(FATAL,"bind socket error");
  53. exit(BIND_ERR);
  54. }
  55. LogMessage(NORMAL,"bind socket success");
  56. //3、设置监听状态
  57. if(-1==listen(_listenSocket,gbacklog))
  58. {
  59. LogMessage(FATAL,"listen socket error");
  60. exit(LISTEN_ERR);
  61. }
  62. LogMessage(NORMAL,"listen socket success");
  63. }
  64. void Start()//启动服务器
  65. {
  66. while(1)
  67. {
  68. //4、服务器获取客户端连接请求
  69. struct sockaddr_in peer;//输出型参数,拿到客户端的信息
  70. socklen_t len=sizeof(peer);
  71. int sock=accept(_listenSocket,(struct sockaddr*)&peer,&len);
  72. if(-1==sock)
  73. {
  74. LogMessage(ERROR,"accept error,next");
  75. continue;
  76. }
  77. LogMessage(NORMAL,"accept a new link success");
  78. // //5、使用accept的返回值sock进行通信,均为文件操作
  79. pid_t id=fork();
  80. if(id==0)//子进程
  81. {
  82. close(_listenSocket);//子进程的
  83. if(fork()>0) exit(0);//让子进程退出,孙子进程成为孤儿进程,交给1号进程托管回收其退出资源
  84. ServerIO(sock);
  85. close(sock);//必须关闭使用完毕的sock,否则文件描述符泄漏(虽然下一句代码exit(0),孙子进程退出也会释放文件描述符,最好还是手动关一下)
  86. exit(0);
  87. }
  88. close(sock);//这是用于通信的套接字fd,父进程和孙子进程都有这个文件描述符,父进程关了,该文件描述符引用技术-1,直至孙子进程退出,该fd才会减为0,关闭
  89. //父进程
  90. //waitpid()
  91. pid_t ret=waitpid(id,nullptr,0);//这里不能用非阻塞等待,否则父进程先跑去执行其他代码,可能会被卡在accept出不来了(没有新的客户端来连接的话)
  92. if(ret>0)
  93. {
  94. std::cout<<"waitsucceess"<<ret<<std::endl;
  95. }
  96. }
  97. }
  98. void ServerIO(int sock)
  99. {
  100. char buffer[1024];
  101. while(1)
  102. {
  103. //服务器读取客户端数据,通过套接字sock这个文件描述符读取数据
  104. ssize_t n=read(sock,buffer,sizeof(buffer)-1);
  105. if(n>0)
  106. {
  107. buffer[n]=0;
  108. std::cout<<"recv message:"<<buffer<<std::endl;
  109. std::string outBuffer=buffer;
  110. outBuffer+="[server echo]";
  111. //服务器将数据处理后发送回客户端
  112. write(sock,outBuffer.c_str(),outBuffer.size());
  113. }
  114. else if(0==n)//服务器read返回值为0,说明客户端关闭了
  115. {
  116. LogMessage(NORMAL,"client quit,server quit");
  117. break;
  118. }
  119. }
  120. }
  121. ~TcpServer()
  122. {}
  123. private:
  124. int _listenSocket;//监听客户端的连接请求,不用于数据通信
  125. uint16_t _port;//服务器端口号
  126. std::string _ip;//服务器ip地址
  127. };
  128. }

区别在于这张图里的代码:

1、close(_listenSocket):关闭子进程的监听fd(虽然手动关不关都行,因为下一句代码就让子进程退出了,最好还是手动关一下)

2、if(fork()>0) exit(0):让子进程创建孙子进程,子进程退出。提前干掉子进程,这样父进程在外部就可以不用阻塞式等待子进程退出了。同时孙子进程成为孤儿进程,会被1号进程领养,程序员无需关心孤儿进程的退出善后工作。

3、孙子进程close(sock):必须关闭使用完毕的sock,否则文件描述符泄漏(虽然下一句代码exit(0),孙子进程退出也会释放文件描述符,最好还是手动关一下)

4、父进程close(sock):这是用于通信的套接字fd,父进程和孙子进程都有这个文件描述符,父进程关了,该文件描述符引用计数-1,直至孙子进程退出,该fd才会减为0,关闭,所以父进程提前关闭该fd不会影响孙子进程。但是这里父进程如果不关,客户端连一个fd+1,存在文件描述符泄露。

5、pid_t ret=waitpid(id,nullptr,0):这里不能用非阻塞等待,否则父进程先跑去执行其他代码,可能会被卡在accept出不来了(如果没有新的客户端来连接的话,将一直卡在accept)

3、多线程版的TCP客户端、服务器

更换tcpServer.hpp即可,其他文件和单进程版一样。

  1. #pragma once
  2. #include <iostream>
  3. #include <unistd.h>
  4. #include <sys/types.h>
  5. #include <sys/wait.h>
  6. #include <sys/socket.h>
  7. #include <arpa/inet.h>
  8. #include <netinet/in.h>
  9. #include <string>
  10. #include <cstring>
  11. #include <cstdlib>
  12. #include <pthread.h>
  13. #include "log.hpp"
  14. namespace Server
  15. {
  16. enum
  17. {
  18. USAGE_ERR=1,
  19. SOCKET_ERR,
  20. BIND_ERR,
  21. LISTEN_ERR,
  22. };
  23. static const uint16_t gport=8080;//缺省的端口号
  24. static const int gbacklog=5;//最大连接数=5+1
  25. const static std::string defaultIp="0.0.0.0";//缺省的IP
  26. class TcpServer;
  27. struct ThreadData//用于线程函数传参
  28. {
  29. ThreadData(TcpServer* self,const int& sock)
  30. :_self(self)
  31. ,_sock(sock)
  32. {}
  33. TcpServer* _self;//this
  34. int _sock;//通信fd
  35. };
  36. class TcpServer
  37. {
  38. public:
  39. TcpServer(const uint16_t& port=gport,const std::string& ip=defaultIp )
  40. :_listenSocket(-1)
  41. ,_port(port)
  42. ,_ip(ip)
  43. {
  44. }
  45. void InitServer()//初始化服务器
  46. {
  47. //1、创建sockrt套接字
  48. _listenSocket=socket(AF_INET,SOCK_STREAM,0);
  49. if(_listenSocket<0)
  50. {
  51. LogMessage(FATAL,"create socket error");
  52. exit(SOCKET_ERR);
  53. }
  54. LogMessage(NORMAL,"create socket success");
  55. //2、绑定端口号+ip地址
  56. struct sockaddr_in local;
  57. memset(&local,0,sizeof(local));
  58. local.sin_addr.s_addr=inet_addr(_ip.c_str());
  59. local.sin_family=AF_INET;
  60. local.sin_port=htons(_port);
  61. if(bind(_listenSocket,(struct sockaddr*)&local,sizeof(local))<0)
  62. {
  63. LogMessage(FATAL,"bind socket error");
  64. exit(BIND_ERR);
  65. }
  66. LogMessage(NORMAL,"bind socket success");
  67. //3、设置监听状态
  68. if(-1==listen(_listenSocket,gbacklog))
  69. {
  70. LogMessage(FATAL,"listen socket error");
  71. exit(LISTEN_ERR);
  72. }
  73. LogMessage(NORMAL,"listen socket success");
  74. }
  75. void Start()//启动服务器
  76. {
  77. while(1)
  78. {
  79. //4、服务器获取客户端连接请求
  80. struct sockaddr_in peer;//输出型参数,拿到客户端的信息
  81. socklen_t len=sizeof(peer);
  82. int sock=accept(_listenSocket,(struct sockaddr*)&peer,&len);
  83. if(-1==sock)
  84. {
  85. LogMessage(ERROR,"accept error,next");
  86. continue;
  87. }
  88. LogMessage(NORMAL,"accept a new link success");
  89. //5、使用accept的返回值sock进行通信,均为文件操作
  90. //多线程版
  91. pthread_t tid;
  92. ThreadData* td=new ThreadData(this,sock);
  93. pthread_create(&tid,nullptr,threadRoutine,(void*)td);
  94. }
  95. }
  96. static void* threadRoutine(void* args)
  97. {
  98. pthread_detach(pthread_self());//线程分离
  99. ThreadData* td=static_cast<ThreadData*>(args);
  100. td->_self->ServerIO(td->_sock);//线程调用服务函数
  101. close(td->_sock);
  102. delete td;
  103. return nullptr;
  104. }
  105. void ServerIO(int sock)
  106. {
  107. char buffer[1024];
  108. while(1)
  109. {
  110. //服务器读取客户端数据,通过套接字sock这个文件描述符读取数据
  111. ssize_t n=read(sock,buffer,sizeof(buffer)-1);
  112. if(n>0)
  113. {
  114. buffer[n]=0;
  115. std::cout<<"recv message:"<<buffer<<std::endl;
  116. std::string outBuffer=buffer;
  117. outBuffer+="[server echo]";
  118. //服务器将数据处理后发送回客户端
  119. write(sock,outBuffer.c_str(),outBuffer.size());
  120. }
  121. else if(0==n)//服务器read返回值为0,说明客户端关闭了
  122. {
  123. LogMessage(NORMAL,"client quit,server quit");
  124. break;
  125. }
  126. }
  127. }
  128. ~TcpServer()
  129. {}
  130. private:
  131. int _listenSocket;//监听客户端的连接请求,不用于数据通信
  132. uint16_t _port;//服务器端口号
  133. std::string _ip;//服务器ip地址
  134. };
  135. }

        在一个进程中的所有线程都可以访问到文件描述符表,属于共享资源,一个线程所对应的fd在使用完毕后需要进行关闭。

4、线程池版的TCP客户端、服务器

        其他文件和单进程版一样。

4.1tcpServer.hpp

  1. #pragma once
  2. #include <iostream>
  3. #include <unistd.h>
  4. #include <sys/types.h>
  5. #include <sys/wait.h>
  6. #include <sys/socket.h>
  7. #include <arpa/inet.h>
  8. #include <netinet/in.h>
  9. #include <string>
  10. #include <cstring>
  11. #include <cstdlib>
  12. #include <pthread.h>
  13. #include "log.hpp"
  14. #include "Task.hpp"
  15. #include "ThreadPool.hpp"
  16. namespace Server
  17. {
  18. enum
  19. {
  20. USAGE_ERR=1,
  21. SOCKET_ERR,
  22. BIND_ERR,
  23. LISTEN_ERR,
  24. };
  25. static const uint16_t gport=8080;//缺省的端口号
  26. static const int gbacklog=5;//最大连接数=5+1
  27. const static std::string defaultIp="0.0.0.0";//缺省的IP
  28. class TcpServer;
  29. struct ThreadData//用于线程函数传参
  30. {
  31. ThreadData(TcpServer* self,const int& sock)
  32. :_self(self)
  33. ,_sock(sock)
  34. {}
  35. TcpServer* _self;//this
  36. int _sock;//通信fd
  37. };
  38. class TcpServer
  39. {
  40. public:
  41. TcpServer(const uint16_t& port=gport,const std::string& ip=defaultIp )
  42. :_listenSocket(-1)
  43. ,_port(port)
  44. ,_ip(ip)
  45. {
  46. }
  47. void InitServer()//初始化服务器
  48. {
  49. //1、创建sockrt套接字
  50. _listenSocket=socket(AF_INET,SOCK_STREAM,0);
  51. if(_listenSocket<0)
  52. {
  53. LogMessage(FATAL,"create socket error");
  54. exit(SOCKET_ERR);
  55. }
  56. LogMessage(NORMAL,"create socket success");
  57. //2、绑定端口号+ip地址
  58. struct sockaddr_in local;
  59. memset(&local,0,sizeof(local));
  60. local.sin_addr.s_addr=inet_addr(_ip.c_str());
  61. local.sin_family=AF_INET;
  62. local.sin_port=htons(_port);
  63. if(bind(_listenSocket,(struct sockaddr*)&local,sizeof(local))<0)
  64. {
  65. LogMessage(FATAL,"bind socket error");
  66. exit(BIND_ERR);
  67. }
  68. LogMessage(NORMAL,"bind socket success");
  69. //3、设置监听状态
  70. if(-1==listen(_listenSocket,gbacklog))
  71. {
  72. LogMessage(FATAL,"listen socket error");
  73. exit(LISTEN_ERR);
  74. }
  75. LogMessage(NORMAL,"listen socket success");
  76. }
  77. void Start()//启动服务器
  78. {
  79. //4、线程池初始化
  80. ThreadPool<Task>::getInstance()->run();//线程启动
  81. while(1)
  82. {
  83. //5、服务器获取客户端连接请求
  84. struct sockaddr_in peer;//输出型参数,拿到客户端的信息
  85. socklen_t len=sizeof(peer);
  86. int sock=accept(_listenSocket,(struct sockaddr*)&peer,&len);
  87. if(-1==sock)
  88. {
  89. LogMessage(ERROR,"accept error,next");
  90. continue;
  91. }
  92. LogMessage(NORMAL,"accept a new link success");
  93. ThreadPool<Task>::getInstance()->push(Task(sock,ServerIO));
  94. }
  95. }
  96. ~TcpServer()
  97. {}
  98. private:
  99. int _listenSocket;//监听客户端的连接请求,不用于数据通信
  100. uint16_t _port;//服务器端口号
  101. std::string _ip;//服务器ip地址
  102. };
  103. }

4.2ThreadPool.hpp 

  1. #pragma once
  2. #include <vector>
  3. #include <queue>
  4. #include <pthread.h>
  5. #include <unistd.h>
  6. #include <mutex>
  7. #include "Thread.hpp"
  8. #include "LockGuard.hpp"
  9. using namespace ThreadNs;
  10. const int gnum =5;
  11. template <class T>//声明
  12. class ThreadPool;
  13. template <class T>
  14. struct ThreadData
  15. {
  16. ThreadData(ThreadPool<T>* tp,const std::string& s)
  17. :_threadPool(tp)
  18. ,_name(s)
  19. {}
  20. ThreadPool<T>* _threadPool;
  21. std::string _name;
  22. };
  23. template <class T>
  24. class ThreadPool
  25. {
  26. private:
  27. //因为普通成员函数第一个参数是this指针,和回调方法不匹配,故改成static类型
  28. static void* handlerTask(void* args)//args是ThreadData对象指针
  29. {
  30. ThreadData<T>* td=static_cast<ThreadData<T>*>(args);
  31. while(1)
  32. {
  33. T t;
  34. { //RAII,出了作用域LockGuard会销毁,将析构锁
  35. LockGuard lockGuard(td->_threadPool->mutex());//加锁
  36. while(td->_threadPool->IsQueueEmpty())//如果队列为空,则等待
  37. {
  38. td->_threadPool->ThreadWait();
  39. }
  40. //线程能走到这里,说明队列一定有任务给线程
  41. t=td->_threadPool->Pop();//从队列中取出任务
  42. }
  43. t();//Task的operator()
  44. }
  45. delete td;//析构ThreadData对象
  46. return nullptr;
  47. }
  48. ThreadPool(const int& num=gnum)
  49. :_num(num)
  50. {
  51. pthread_mutex_init(&_mutex,nullptr);
  52. pthread_cond_init(&_cond,nullptr);
  53. //创建线程
  54. for(int i=0;i<_num;++i)
  55. {
  56. _threads.push_back(new Thread());
  57. }
  58. }
  59. ThreadPool(const ThreadPool<T>&)=delete;//禁用拷贝构造
  60. ThreadPool<T>& operator=(const ThreadPool<T>&)=delete;//禁用赋值运算符重载
  61. public://解决静态handlerTask是静态函数的问题,这几个都是偷家函数
  62. void LockQueue() {pthread_mutex_lock(&_mutex);}
  63. void UnLockQueue() {pthread_mutex_unlock(&_mutex);}
  64. bool IsQueueEmpty(){return _taskQueue.empty();}
  65. void ThreadWait() {pthread_cond_wait(&_cond,&_mutex);}
  66. T Pop()
  67. {
  68. T t=_taskQueue.front();
  69. _taskQueue.pop();
  70. return t;
  71. }
  72. pthread_mutex_t* mutex()
  73. {
  74. return &_mutex;
  75. }
  76. public:
  77. void run()//线程启动
  78. {
  79. for(const auto& t:_threads)
  80. {
  81. ThreadData<T>* td=new ThreadData<T>(this,t->threadName());
  82. t->start(handlerTask,(void*)td);
  83. std::cout<<t->threadName()<<"start..."<<std::endl;
  84. }
  85. }
  86. void push(const T& in)
  87. {
  88. //RAII,出了作用域,锁将会被释放
  89. LockGuard lockGuard(&_mutex);
  90. _taskQueue.push(in);
  91. pthread_cond_signal(&_cond);
  92. std::cout<<"任务发送成功"<<std::endl;
  93. }
  94. ~ThreadPool()
  95. {
  96. pthread_mutex_destroy(&_mutex);
  97. pthread_cond_destroy(&_cond);
  98. for(const auto& t:_threads)
  99. {
  100. delete t;
  101. }
  102. }
  103. static ThreadPool<T>* getInstance()//这里的static的作用是让这个函数只有一份,获取单例对象。tp是临界资源,需要加锁
  104. {
  105. if(nullptr==tp)//因为锁只创建一次,防止线程进来被锁阻塞
  106. {
  107. //只进来一次就够了
  108. _singletonLock.lock();
  109. if(nullptr==tp)//说明对象还没有被创建
  110. {
  111. tp=new ThreadPool<T>();
  112. }
  113. _singletonLock.unlock();
  114. }
  115. return tp;
  116. }
  117. private:
  118. int _num;//线程个数
  119. std::vector<Thread*> _threads;//使用vector存放线程
  120. std::queue<T> _taskQueue;//任务队列,往里面放任务,它是共享资源,需要加锁保护
  121. pthread_mutex_t _mutex;//互斥锁
  122. pthread_cond_t _cond;//条件变量
  123. static ThreadPool<T>* tp;//单例模式静态的对象指针
  124. static std::mutex _singletonLock;//获取单例对象使用的锁
  125. };
  126. template <class T>
  127. ThreadPool<T>* ThreadPool<T>::tp=nullptr;
  128. template <class T>
  129. std::mutex ThreadPool<T>::_singletonLock;

4.3Task.hpp

  1. #pragma once
  2. #include <iostream>
  3. #include <functional>
  4. #include <string>
  5. void ServerIO(int sock)
  6. {
  7. char buffer[1024];
  8. while(1)//适应快速响应的任务,这个任务while其实不太合适
  9. {
  10. //服务器读取客户端数据,通过套接字sock这个文件描述符读取数据
  11. ssize_t n=read(sock,buffer,sizeof(buffer)-1);
  12. if(n>0)
  13. {
  14. buffer[n]=0;
  15. std::cout<<"recv message:"<<buffer<<std::endl;
  16. std::string outBuffer=buffer;
  17. outBuffer+="[server echo]";
  18. //服务器将数据处理后发送回客户端
  19. write(sock,outBuffer.c_str(),outBuffer.size());
  20. }
  21. else if(0==n)//服务器read返回值为0,说明客户端关闭了
  22. {
  23. close(sock);
  24. LogMessage(NORMAL,"client quit,server quit");
  25. break;
  26. }
  27. }
  28. }
  29. class Task
  30. {
  31. //using func_t=std::function<int(int,int,char)>;
  32. typedef std::function<void(int)> func_t;//函数对象
  33. public:
  34. Task()
  35. {}
  36. Task(int sock,func_t func)
  37. :_sock(sock)
  38. ,_callBack(func)
  39. {}
  40. void operator()()//消费者调用
  41. {
  42. _callBack(_sock);
  43. }
  44. private:
  45. int _sock;
  46. func_t _callBack;//回调函数
  47. };

5、守护进程+多线程版的TCP客户端、服务器

        其他文件和单进程版一样。

5.1daemon.hpp

  1. #pragma once
  2. #include <iostream>
  3. #include <unistd.h>
  4. #include <signal.h>
  5. #include <cstdlib>
  6. #include <cassert>
  7. #include <sys/types.h>
  8. #include <sys/stat.h>
  9. #include <fcntl.h>
  10. #define DEV "/dev/null"//数据黑洞,向它写入的数据会被吃掉,读取数据什么都读不到(不会使进程退出)
  11. void DaemonSele(const char* currrPath=nullptr)
  12. {
  13. //1、让调用进程屏蔽异常的信号
  14. //SIGPIPE信号会在进程向一个已经关闭的socket连接写数据时产生,如果不处理这个信号,进程会被强制退出。通过忽略SIGPIPE信号,可以避免进程因为这个信号而退出。
  15. signal(SIGPIPE,SIG_IGN);
  16. //2、让自己不是组长,调用setsid
  17. if(fork()>0) exit(0);//守护进程也称精灵进程,本质就是一个孤儿进程
  18. pid_t n=setsid();
  19. assert(n!=-1);//失败返回-1
  20. //3、守护进程脱离终端,所以要关闭或重定向进程默认打开的文件及文件描述符
  21. int fd=open(DEV,O_RDWR);//以读写的方式打开文件黑洞
  22. if(fd>=0)//创建成功:重定向
  23. {
  24. dup2(fd,0);//将fd覆盖标准输入
  25. dup2(fd,1);
  26. dup2(fd,2);
  27. close(fd);
  28. }
  29. else//创建失败:手动关闭文件描述符
  30. {
  31. close(0);
  32. close(1);
  33. close(2);
  34. }
  35. //4、进程执行路径更改(可改可不改)
  36. if(currrPath)
  37. {
  38. chdir(currrPath);
  39. }
  40. }

5.2tcpServer.cc

  1. #include "tcpServer.hpp"
  2. #include "memory"
  3. #include "daemon.hpp"
  4. using namespace Server;
  5. static void Usage(std::string proc)
  6. {
  7. std::cout<<"Usage:\n\t"<<proc<<"serverPort\n\n";
  8. }
  9. //./tcpServer local_port
  10. int main(int argc,char* argv[])
  11. {
  12. if(argc!=2)//判断外部传入的参数是否为2
  13. {
  14. Usage(argv[0]);
  15. exit(USAGE_ERR);
  16. }
  17. uint16_t port=std::stoi(argv[1]);
  18. std::unique_ptr<TcpServer> tsvr(new TcpServer(port));
  19. tsvr->InitServer();
  20. DaemonSele();//守护进程化,让这个独立的孤儿进程去启动服务器
  21. tsvr->Start();
  22. return 0;
  23. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/188248
推荐阅读
相关标签
  

闽ICP备14008679号