TCP套接字编写,多进程多线程版本 Linux网络通信( 七 )

void Request(){string msg;while (1){cout << "Please Enter# ";getline(cin, msg);write(_sock, msg.c_str(), msg.size());char buf[256];ssize_t size = read(_sock, buf, sizeof(buf)-1);if (size <= 0){cerr << "read error" << endl;exit(-1);}buf[size] = 0;cout << buf << endl;}}不同版本的服务端服务代码多进程版本思路: 为了给不同的连接提供服务,所以我们需要让父进程去不断获取连接,获取连接后,让父进程创建一个子进程去为这个获取到的连接提供服务,那么问题来了,子进程去服务连接,父进程是否需要等待子进程?按常理来说,是需要的,如果不等待的话,子进程退出,子进程的资源就没有人回收,就变成僵尸进程了,如果父进程等待子进程的话,父进程就需要阻塞在哪,无法去获取到新的连接,这也是不完全可行的,所以就有了一下两种解决方案:

  • 1.通过注册SIGCHLD(子进程退出会想父进程发起该信号)信号,把它的处理信号的方式改成SIG_IGN(忽略),此时子进程退出就会自动清理资源不会产生僵尸进程,也不会通知父进程,这种方法比较推荐,也比较简单粗暴
  • 2.通过创建子进程,子进程创建孙子进程,子进程直接退出,让1号进程领养孙子进程,这样父进程只需要等很短的时间就可以回收子进程的资源,这样父进程可以继续去获取连接,孙子进程给连接提供服务即可
    TCP套接字编写,多进程多线程版本 Linux网络通信

    文章插图
方法一代码编写:
void loop(){// 对SIGCHLD信号进行注册,处理方式为忽略signal(SIGCHLD, SIG_IGN);struct sockaddr_in peer;// 获取远端端口号和ip信息socklen_t len = sizeof(peer);while (1){// 获取链接// sock 是进行通信的一个套接字_listen_sock 是进行监听获取链接的一个套接字int sock = accept(_listen_sock, (struct sockaddr*)&peer, &len);if (sock < 0){cout << "accept fail, continue accept" << endl;continue;}// 创建子进程pid_t id = fork();if (id == 0){//子进程,通信的工作交给子进程,父进程只负责监听close(_listen_sock);//可以不关闭,但是建议关闭,防止后期子进程对监听套接字进行一些操作,影响父进程//在前面的博客中讲过,父子进程共享文件表,对文件进行读写操作会影响彼此,但是由于子进程有自己的PCB,有自己的文件表项,关闭自己进程的文件描述符不会造成影响int peerPort = ntohs(peer.sin_port);string peerIp = inet_ntoa(peer.sin_addr);cout << "get a new link, [" << peerIp << "]:[" << peerPort<< "]"<< endl;Server(peerIp, peerPort, sock);}// 父进程继续去获取连接}}void Server(string ip, int port, int sock){while (1){char buf[256];ssize_t size = read(sock, buf, sizeof(buf)-1);if (size > 0){// 正常读取size字节的数据buf[size] = 0;cout << "[" << ip << "]:[" << port<< "]# "<< buf <<endl;string msg = "server get!-> ";msg += buf;write(sock, msg.c_str(), msg.size());}else if (size == 0){// 对端关闭cout << "[" << ip << "]:[" << port<< "]# close" << endl;break;}else{// 出错cerr << sock << "read error" << endl;break;}}close(sock);cout << "service done" << endl;// 子进程退出exit(0);}完整版代码:
#include<iostream>#include<string.h>#include<unistd.h>#include<sys/types.h>#include<sys/socket.h>#include<arpa/inet.h>#include<signal.h>#include<pthread.h>using namespace std;#define DEFAULT_PORT 8080#define BACK_LOG 5class TcpServer{public:TcpServer(int port = DEFAULT_PORT):_port(port),_listen_sock(-1){ }~TcpServer(){if(_listen_sock>=0){close(_listen_sock);}} public://创建套接字bool TcpServerInit(){//创建套接字_listen_sock = socket(AF_INET,SOCK_STREAM,0);if(_listen_sock<0){cout<<"套接字创建失败"<<endl;return false;}cout<<"套接字创建成功,sock:"<<_listen_sock<<endl;//绑定端口号struct sockaddr_in local;memset(&local,0,sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = INADDR_ANY;if(bind(_listen_sock,(struct sockaddr *)&local,sizeof(local))<0){cout<<"绑定失败"<<endl;return false;}cout<<"绑定成功"<<endl;//将套接字设置成监听套接字if(listen(_listen_sock,BACK_LOG)<0){cout<<"监听套接字创建失败"<<endl;return false;}cout<<"监听套接字创建成功"<<endl;return true; }//循环获取连接void loop(){//对信号SIGCHLD信号进行注册,处理方式为忽略,子进程结束的时候会交由内核处理signal(SIGCHLD,SIG_IGN);struct sockaddr_in peer;//获取客户端的端口号和ipsocklen_t len = sizeof(peer);while(1){//获取连接//sock是进行通信的一个套接字,_listen_sock是用来监听的套接字int sock = accept(_listen_sock,(struct sockaddr *)&peer,&len);if(sock<0){cout<<"accept fail,continue accept"<<endl;continue;}//创建子进程pid_t id = fork();if(id == 0){//子进程close(_listen_sock);//可以不关闭,但是建议关闭,防止后期子进程对监听套接字进行一些操作,影响父进程//在前面的博客中讲过,父子进程共享文件表,对文件进行读写操作会影响彼此,但是由于子进程有自己的PCB,有自己的文件表项,关闭自己进程的文件描述符不会造成影响int peerPort = ntohs(peer.sin_port);string peerIp = inet_ntoa(peer.sin_addr);cout<<"获得了一个新的连接,["<< peerIp <<"]:["<< peerPort <<"]"<<endl;this->Server(peerIp,peerPort,sock);}//父进程继续取获取连接}}void Server(string ip,int port,int sock){while(1){char buf[256];ssize_t size = read(sock,buf,sizeof(buf)-1);if (size > 0){// 正常读取size字节的数据buf[size] = 0;cout << "[" << ip << "]:[" << port<< "]# "<< buf <<endl;string msg = "server get!-> ";msg += buf;write(sock, msg.c_str(), msg.size());}else if (size == 0){// 对端关闭cout << "[" << ip << "]:[" << port<< "]# close" << endl;break;}else{// 出错cout << sock << "read error" << endl;break;}}close(sock);cout<<"server done"<<endl;//子进程退出exit(0);}private:int _port;int _listen_sock;};int main(int argc,char* argv[]){if (argc != 2){cout << "Usage:" << argv[0] << "port:" << endl;exit(-1);}int port = atoi(argv[1]);TcpServer* usr = new TcpServer(port);usr->TcpServerInit();usr->loop();delete usr;system("pause");return EXIT_SUCCESS;}

经验总结扩展阅读