socket编程(6)
本章主要任务;
1.继续晚上TCP回射客户/服务器程序
2.再次强调TCP是个流协议
3.僵死进程与SIGCHLD信号
一、TCP回射客户/服务器程序

第一种会粘包,第二种用在行位加\n。readline.
第三种解决粘包问题就是使用,定长包,但是这种方式效率低,浪费缓冲区。改进机制是我们可以在包中加入包长信息,之前讲过了。
二、TCP是个流协议

三、僵死进程与SIGCHLD信号
1.可以signal(SIGCHLD,SIG_IGN)忽略信号,来避免僵死进程
2.捕捉该信号来避免僵死进程 signal(SIGCHLD,handle_sigchld)
在handle_sigchld函数中使用wait函数。原型如下:
pid_t wait(int *status);
这里我们不关心退出状态 所以用 wati(NULL);
但是使用wait 有一个问题,就当有多个子进程都退出时,wait仅仅等待第一个子进程退出及返回了
这时候就用到 waitpid。函数原型如下:
pid_t waitpid(pid_t pid, int *status, int options);
在这之前,我们先模拟一下5个客户端连接服务器,并且同时退出的情况
我们对客户端程序进行一下更改,创建5个套接字。
1 int main() 2 { 3 /*创建套接字*/ 4 int sockfd[5]; 5 int i; 6 for(i=0;i<5;i++) 7 { 8 if((sockfd[i] = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0) 9 printf("socket err"); 10 11 /*serv addr and port*/ 12 struct sockaddr_in servaddr; 13 memset(&servaddr,0,sizeof(servaddr)); 14 servaddr.sin_family = AF_INET; 15 servaddr.sin_port = htons(5188); 16 servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 17 18 /*连接,一旦连接成功,sockfd套接字就处于已连接状态,从逻辑上 19 是与服务器端的conn套接字连接*/ 20 if(connect(sockfd[i],(struct sockaddr*)&servaddr,sizeof(servaddr)) <0) 21 printf("connect err"); 22 struct sockaddr_in localaddr; 23 socklen_t addrlen = sizeof(localaddr); 24 if(getsockname(sockfd[i],(struct sockaddr*)&localaddr,&addrlen)<0) 25 ERR_EXIT("getsockname"); 26 printf("ip=%s port=%d\n",inet_ntoa(localaddr.sin_addr),ntohs(localaddr.sin_port)); 27 } 28 echo_cli(sockfd[0]); 29 30 return 0; 31 }
这里我们创建五个套接字与服务器相连,这里只让第一个套接字跟服务器端通信

启动服务器,启动客户端
这时候我们发现有五个客户端与服务器连接
如图:

现在讲客户端退出
我们发现出现了4个僵死进程。
如图

这时候我们可以用waitpid来解决
waitpid(-1,NULL,WNOHANG);
1 void handle_sigchld(int sig) 2 { 3 //wait(NULL); 4 while(waitpid(-1,NULL,WNOHANG) >0) 5 ; 6 7 }
我们发现有时候是三个有时候是四个僵死进程。
这是因为5个子进程同时向父进程发送信号,父进程在处理第一个信号是,其他到达信号会被忽略。所以是不可靠信号。
这个跟FIN到达的时间有关。
那么我们能不能解决这个问题呢???其实很简单,我们用一个while循环来解决。这里是一个死循环
while(waitpid(-1,NULL,WNOHANG)>0)
;
大于0表示成功处理一个子进程,
那么循环退出的条件呢? 当没有子进程退出的时候,我们这边指定的是WNOHANG就是不挂起。没有子进程退出就会放回0就会退出死循环
通过这种方式,我们就完美的解决了僵死进程。我们推荐用waitpid,而不使用第一种signal(SIGCHLD,SIG_IGN的方式,因为我们可以处理更多的事情。

浙公网安备 33010602011771号