[第二版]多线程的发送与接收

三个基本问题

  1. 服务器请求处理完毕, 处理僵尸进程
  2. 慢系统调用被SIGCHLD信号中断
  3. 多个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;
}
posted @ 2016-09-18 22:47  cfans1993  阅读(289)  评论(0编辑  收藏  举报