[第二版]多线程的发送与接收
三个基本问题
- 服务器请求处理完毕, 处理僵尸进程
- 慢系统调用被SIGCHLD信号中断
- 多个SIGCHLD同时到达的情况
wait和waitpid
子进程终止时会产生SIGCHLD信号并发送给父进程
父进程可以捕获处理该信号也可以不捕获
SIGCHLD只是个数值信号, 并不包含结束子进程的pid, wait和waitpid需要通过遍历子进程来判断是哪个子进程结束.
书上关于waitpid与wait两个版本主要是两个方面的不同:
a. wait没有nowait选项, 而waitpid有nowait选项(WNOHANG)
b. waitpid版本加了个while循环. 如果多个SIGCHLD同时到达, wait用while请求也会处理完所有僵死进程, 但如果还有子进程未结束, 由于wait没有nowait选项, 所以会等待所有子进程结束再返回(在wait时无法接入新的客户端). 而waitpid用while循环, 由于参数-1和nowait选项, 处理完所有僵死进程后就立即返回.
accept被中断
经过测试在现在linux系统上, accept是可重入函数且signal函数已经自带重入属性, 如果要模拟被SIGCHLD中断可以自己写一个信号处理函数
Sigfunc *Signal(int signo,Sigfunc *func){
/* test the different between system's signal and user's signal function */
struct sigaction act,oact;
act.sa_handler=func;
sigemptyset(&act.sa_mask);
act.sa_flags=0;
if(sigaction(signo,&act,&oact) <0)
err_quit("sigaction error");
return(oact.sa_handler);
}
client.c
#include "unp.h"
void str_cli(FILE *fp,int sockfd);
int main(int argc,char *argv[]){
int sockfd[5],i;
struct sockaddr_in servaddr;
if(argc != 2)
err_quit("usage: client <ip address>");
for(i=0;i<5;i++){
sockfd[i]=Socket(AF_INET,SOCK_STREAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(13);
inet_pton(AF_INET,argv[1],&servaddr.sin_addr);
Connect(sockfd[i],(struct sockaddr *)&servaddr,sizeof(servaddr));
}
str_cli(stdin,sockfd[0]);
exit(0);
}
void str_cli(FILE *fp,int sockfd){
char sendline[MAXLINE],recvline[MAXLINE];
int n;
while(Fgets(sendline,MAXLINE,fp) != NULL){
writen(sockfd,sendline,strlen(sendline));
if((n=read(sockfd,recvline,MAXLINE)) < 0)
err_quit("str_cli: server terminated permaturely");
recvline[n]=0;
Fputs(recvline,stdout);
}
}
serv.c
#include "unp.h"
void str_echo(int sockfd);
void sig_chld(int signo);
int main(int argc, char *argv[]){
int listenfd,connfd;
pid_t childpid;
socklen_t len;
struct sockaddr_in servaddr,cliaddr;
Signal(SIGCHLD,sig_chld);
listenfd=Socket(AF_INET,SOCK_STREAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
servaddr.sin_port=htons(13);
Bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
Listen(listenfd,10);
for(;;){
len=sizeof(cliaddr);
//如果系统signal函数打开重入选项, 此处可用自定义的Accept
if((connfd=accept(listenfd,(struct sockaddr *)&cliaddr,&len)) < 0){
if(errno == EINTR)
continue;
else
err_quit("accept error");
}
if((childpid=Fork())==0){
Close(listenfd);
str_echo(connfd);
exit(0);
}
Close(connfd);
}
}
void str_echo(int sockfd){
ssize_t n;
char buf[MAXLINE];
again:
while((n=read(sockfd,buf,MAXLINE)) >0)
writen(sockfd,buf,n);
if(n<0 && errno==EINTR)
goto again;
else if(n < 0)
err_quit("str_echo read error");
}
void sig_chld(int signo){
pid_t pid;
int stat;
while((pid=waitpid(-1,&stat,WNOHANG)) > 0)
printf("child %d terminated\n",pid);
return;
}