首页 > 技术文章 > Ubuntu下实现回射服务器-多进程处理多客户连接(三)

simon-xie 2021-05-28 13:25 原文

在前面的回射服务器中,无法处理多个客户端的连接,原因在于线程中,有一个死循环一直接收客户端发送的消息,accept函数没有机会从listen维护的就绪队列头中获取新的连接。

处理办法:将accept置于一个死循环中不断接收新的连接,当接收新的连接后,开辟一个新的进程,在新进程中处理和客户端的通信。

服务端代码如下:

 1 #include<stdio.h>
 2 #include <sys/types.h>          /* See NOTES */
 3 #include <sys/socket.h>
 4 #include <netinet/ip.h>
 5 #include <errno.h>
 6 #include <stdlib.h>
 7 #include <netinet/in.h>
 8 #include <arpa/inet.h>
 9 #include <unistd.h>
10 #include <string.h>
11 void do_service(int conn){
12 
13     char recvbuffer[1024];
14     while(1){
15         memset(recvbuffer,0,sizeof(recvbuffer));
16         int readsize=read(conn,&recvbuffer,sizeof(recvbuffer));
17         if (readsize==0){
18             perror("client disconnect");
19             break;//跳出循环
20         }else if (readsize==-1){
21               perror("read");
22           exit(EXIT_FAILURE);
23         }
24         fputs(recvbuffer,stdout);
25         write(conn,recvbuffer,strlen(recvbuffer));
26     }
27 }
28 int main(){
29     int socketfd;
30     //建立连接套接字
31     socketfd=socket(PF_INET,SOCK_STREAM,0);
32     if(socketfd<0){
33         perror("socket");
34         exit(EXIT_FAILURE);
35     }
36 
37     //初始化服务端地址
38     struct sockaddr_in sockaddress;
39     sockaddress.sin_family=AF_INET;
40     sockaddress.sin_port=htons(5188);
41     sockaddress.sin_addr.s_addr=htonl(INADDR_ANY);//INADDR_ANY表示接收任何地址的连接
42     //sockaddress.sin_addr.s_addr=inet_addr("127.0.0.1");//也可以是这种方式
43 
44     int on=1;
45     //设置地址重用
46     if (setsockopt(socketfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))<0){
47         perror("setsockopt");
48         exit(EXIT_FAILURE);
49     }
50 
51     //将初始化服务端地址绑定到当前套接字
52     if(bind(socketfd,(struct sockaddr*)&sockaddress,sizeof(sockaddress))<0){
53         perror("bind");
54         exit(EXIT_FAILURE);
55     }
56 
57     //将套接字转换为监听状态,当前套接字转换为被动套接字
58     if ( listen(socketfd,SOMAXCONN)<0){
59         perror("listen");
60         exit(EXIT_FAILURE);
61     }
62 
63     int conn;
64     struct sockaddr_in peeraddr;//对端地址
65     socklen_t len=sizeof(struct sockaddr_in);//对端地址长度,必须提前获得
66     pid_t pid;//进程id号
67     while(1){
68         //从对头取完成三次握手的节点,返回的conn不同于前面的监听套接字,该套接字被称为通信套接字,也就是主动套接字
69         conn=accept(socketfd,(struct sockaddr*)&peeraddr,&len);
70         if (conn<0){
71             perror("accept");
72             exit(EXIT_FAILURE);
73         }
74         printf("client ip:%s port:%d\n",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));
75         pid=fork();
76         if (pid<0){//开启进程失败
77             perror("fork");
78             exit(EXIT_FAILURE);
79         }else if(pid==0){//进入子进程处理过程
80             close(socketfd);//子进程不需要监听套接字,关闭它
81             do_service(conn);
82             exit(EXIT_SUCCESS);//do_service被break后,子进程就没有存在的必要了,直接退出
83         }else{//主进程处理过程
84             close(conn);//主进程执行到这后就不需要通信套接字了,因为子进程负责进行通信过程,关闭它
85         }
86     }
87     return 0;
88 
89 }

 

推荐阅读