Ubuntu下实现回射服务器-多进程处理多客户连接(三)
在前面的回射服务器中,无法处理多个客户端的连接,原因在于线程中,有一个死循环一直接收客户端发送的消息,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 }