传输层使用udp实现客户端和服务端通信的小程序 (可以实现多个客户端与服务端通信)

    xiaoxiao2023-10-24  170

     

           因为不知道客户端连接什么时候来,不知道客户端数据什么时候来,因此写死的服务端可能阻塞在accept,或者recv,因此必须用多进程,或者多线程处理已经连接客户端的通信。

           对于多进程,当父进程获取到新的socket后,创建子进程,让子进程负责与该客户端进行通信,因为子进程会复制父进程的套接字文件描述符,而父进程只负责接收新的客户端的连接与其对应的新的文件描述符,此外它还要关闭这个获取的文件描述符,因为父进程不用这个文件描述符,不关掉会占用资源,还有就是父进程要重定义SIGCHLD信号的处理方式,因为当与某个客户端通信的子进程要结束时,会利用该信号通知父进程,而父进程此时要进行wait它,同意os对其资源进行回收。

           对于多线程,当主线程获取到新客户端的socket时,要将其套接字文件描述符传到创建的子线程的入口函数中,让其进行与该客户端进行通信,但是主线程不能关闭该套接字文件描述符,因为线程之间共享文件描述符,主线程关闭导致子线程无法使用,此外创建线程后,之间将其分离,就不用线程等待了。

           以下是多进程版的服务端、多线程版的服务端:

    //服务端 2 3 #include "tcp.hpp" 4 using namespace std; 5 6 void sigcb(int num)//自定义信号处理方式 7 { 8 while(waitpid(-1, NULL, WNOHANG)>0); 9 } 10 11 int main(int argc, char* argv[]) 12 { 13 //检查参数合法性 14 if(argc!=3) 15 { 16 perror("./server ip port"); 17 return 0; 18 } 19 20 //自定义SIGCHLD信号处理方式 21 signal(SIGCHLD, sigcb); 22 23 //组织地址信息 24 string ip=argv[1]; 25 uint16_t port=atoi(argv[2]); 26 27 //创建TcpSocket对象 28 TcpSocket sock; 29 30 //创建socket,绑定地址,开始监听 31 CHECK(sock.Socket()); 32 CHECK(sock.Bind(ip, port)); 33 CHECK(sock.Listen()); 34 35 //获取新的socket,开始接受数据 36 while(1) 37 { 38 TcpSocket clisock;//用于接受新的socket 39 struct sockaddr_in cliaddr; 40 if(sock.Accept(clisock, &cliaddr)==false)//对于一个客户端的新socket接受失败,应该继续上去接受其他客户端的socket 41 { 42 continue; 43 } 44 //来到这说明接收成功了 45 46 //接受到新的socket后,创建子进程,让子进程专门与这个客户端进行通信(子进程复制了父进程的文件描述符,因此直接可以用) 47 int pid=fork(); 48 if(pid==0)//子进程 49 { 50 while(1) 51 { 52 string buf; 53 54 //接受数据 55 clisock.Recv(buf); 56 //打印出接收到的数据 57 cout<<"client say:"<<buf<<endl; 58 59 buf.clear(); 60 //收到客户端数据后,恢复数据给客户端 61 cout<<"server say:"; 62 fflush(stdout); 63 cin>>buf; 64 clisock.Send(buf);//给对应的客户端恢复消息 65 } 66 } 67 //这是父进程,直接关闭这个文件描述符 68 //注册SIGCHLD信号,使得子进程退出后,可以回收资源,否则父进程在这等着,子进程 69 //一直不结束则父进程还是没办法上去继续接受下一个客户端的新连接(即就会一直阻塞在这wait子进程) 70 clisock.Close(); 71 } 72 sock.Close();//关闭套接字 73 74 return 0; 75 } //服务端 2 3 #include "tcp.hpp" 4 using namespace std; 5 6 void* thr_start(void* arg)//线程入口函数 7 { 8 TcpSocket* sock=(TcpSocket*)arg; 9 while(1) 10 { 11 string buf; 12 13 //接受数据 14 sock->Recv(buf); 15 //打印出接收到的数据 16 cout<<"client say:"<<buf<<endl; 17 18 buf.clear(); 19 //收到客户端数据后,恢复数据给客户端 20 cout<<"server say:"; 21 fflush(stdout); 22 cin>>buf; 23 sock->Send(buf);//给对应的客户端恢复消息 24 } 25 sock->Close(); 26 return NULL; 27 } 28 29 int main(int argc, char* argv[]) 30 { 31 //检查参数合法性 32 if(argc!=3) 33 { 34 perror("./server ip port"); 35 return 0; 36 } 37 38 //组织地址信息 39 string ip=argv[1]; 40 uint16_t port=atoi(argv[2]); 41 42 //创建TcpSocket对象 43 TcpSocket sock; 44 45 //创建socket,绑定地址,开始监听 46 CHECK(sock.Socket()); 47 CHECK(sock.Bind(ip, port)); 48 CHECK(sock.Listen()); 49 50 //获取新的socket,开始接受数据 51 TcpSocket* clisock=new TcpSocket;//用于接受新的socket 52 53 while(1) 54 { 55 56 if(sock.Accept(*clisock)==false)//对于一个客户端的新socket接受失败,应该继续上去接受其他客户端的socket 57 { 58 continue; 59 } 60 //来到这说明接收成功了 61 62 //创建线程,让线程去与这个客户端进行通信 63 pthread_t tid; 64 pthread_create(&tid, NULL, thr_start, (void*)clisock);//将新socket描述符传到线程入口函数中去 65 pthread_detach(tid);//将线程分离 66 67 //多线程版本中,主线程不能关闭socket,因为线程间共享文件描述符,而不像进程间的而是独立的 68 69 } 70 sock.Close();//关闭套接字 71 72 return 0; 73 }

     

    最新回复(0)