套接字描述符在多进程和多线程下的共享

一:概述

         在简单的回射服务例子中,客户端和服务器的交互步骤如下:

客户从标准输入中读入一行文本,并写给服务器;

服务器从网络输入读入这行文本,并回射给客户;

客户从网络输入读入这行回射文本,并显示在标准输出上。下图描述了整个过程:

 

二:多进程的str_cli

         其中客户端的str_cli函数处理客户端上的主要逻辑:从标准输入读入一行文本,写到服务器上,然后读会服务器对改行的回射,写到标准输出。

         str_cli的非阻塞版木比较复杂--约有135行代码(P342),与之相比,使用select和阻塞式IO的版木有40行代码(P137),而最初的停-等版本(P100)则只有区区20行代码。

         代码长度从20行倍增到40行的努力是值得的,因为在批最模式下执行速度几乎提高了30倍,而且在阻塞的描述符上使用select不太复杂。然而考虑到结果代码的复杂性,把应用程序编写成使用非阻塞式IO的努力足否照样值得?答案是否定的。每当我们发现需要使用非阻塞式IO时,更简单的办法通常是把应用程序仟务划分到多个迸程〔使用Fork〕或多个线程。

         下面是使用多进程的版本:


#include     "unp.h"                                                 

                                                                     

void  str_cli(FILE  *fp,  int sockfd)                                        

{                                                                     

    pid_t    pid;                                                   

    char    sendline[MAXLINE],  recvline[MAXLINE];                   

                                                                     

    if ( (pid = Fork()) == 0) {   /* child: server -> stdout */      

       while (Readline(sockfd,  recvline,  MAXLINE) > 0)               

           Fputs(recvline,  stdout);                                  

                                                                      

       kill(getppid(),  SIGTERM);   /* in case parent still running */

       exit(0);                                                      

    }                                                                

                                                                      

    /* parent: stdin -> server */                                    

    while (Fgets(sendline,  MAXLINE,  fp) != NULL)                     

        Writen(sockfd,  sendline,  strlen(sendline));                  

                                                                     

    Shutdown(sockfd,  SHUT_WR); /* EOF on stdin, send FIN */         

    pause();                                                         

    return;                                                          

}                                                                    

 

 

         图中明确地指出:所用TCP连接是全双工的,而且父子进程共享同一个套接字:父进程往该套接字中写,子进程从该套接字中读。尽管套接字只有一个,其接收缓冲区和发送缓冲区也分别只有一个,然而这个套接字却有两个描述符在引用它:一个在父进程中,另一个在子进程中。

 

三:多线程的echo服务器

#include     "unpthread.h"                                              

                                                                        

static  void  *doit(void*);      /* each thread executes thisfunction */

                                                                        

int  main(int argc,  char **argv)                                             

{                                                                       

    int    listenfd,  connfd;                                           

    pthread_t  tid;                                                      

    socklen_t  addrlen,  len;                                             

    struct  sockaddr  *cliaddr;                                            

                                                                        

    if (argc == 2)                                                      

        listenfd = Tcp_listen(NULL,  argv[1],  &addrlen);                 

    else if (argc == 3)                                                 

        listenfd = Tcp_listen(argv[1],  argv[2],  &addrlen);              

    else                                                                

        err_quit("usage: tcpserv01 [<host> ] <service or port>");      

                                                                        

    cliaddr = malloc(addrlen);                                          

    for (; ; ) {                                                        

        len = addrlen;                                                  

        connfd = Accept(listenfd,  cliaddr,  &len);                       

        Pthread_create(&tid,  NULL,  &doit, (void *) connfd);             

    }                                                                    

}                                                                       

                                                                        

static void *  doit(void * arg)                                                         

{                                                                       

    Pthread_detach(pthread_self());                                     

    str_echo((int) arg);        /* same function as before */           

    Close((int) arg);           /* done with connected socket*/        

    return (NULL);                                                      

}                                                                                     

 

 

         注意:主线程不关闭已连接套接字,而在调用fork的并发服务器程序中却总是反着做。这是因为同一进程内的所有线程共享全部描述符。要是主线程调用close,它就会终止相应的连接。创建新线程并不影响己打开描述符的引用计数,这一点不同于fork

 

 

摘自《Unix网络编程卷一:套接字联网API》

 

原文地址:https://www.cnblogs.com/gqtcgq/p/7247249.html