• 不为客户连接创建子进程的并发回射服务器( poll实现 )


    前言

      在上文中,我使用select函数实现了不为客户连接创建子进程的并发回射服务器( 点此进入 )。但其中有个细节确实有点麻烦,那就是还得设置一个client数组用来标记select监听描述符集中被设置为监听位的位。有没有方法简化这个处理呢?有!在《UNIX网络编程》第六章最后介绍了一种类似select的函数 --- poll函数,用它来实现IO复用使代码简化了不少( 起码并发回射服务器的例子是的 )。

    poll函数介绍

      1. 包含头文件:<poll.h>

      2. 函数原型:int poll ( struct pollfd * fdarray, unsigned long nfds, int timeout);

    1 // 监听描述数组的元素
    2 struct pollfd {
    3     int fd;    // 监听描述符
    4     short events;    // 测试事件
    5     short revents;    // 测试结果
    6 };

      3. 说明:相比于select函数,poll函数使用了结构体数组fdarray来表示监听描述符集合,该数组元素类型如上代码所示。当我们检索这个数组,我们可以知道有哪些描述符被监听,监听测试事件是什么,测试的结果又是什么。( 而在select函数的fdset描述符集合中,无法获知某位是不是被监听的描述符,这也就是下面的代码并不需要使用client数组的原因 )。

    代码实现

     1 #include    "unp.h"
     2 // 下头文件包含宏定义OPEN_MAX
     3 #include    <limits.h>        
     4 
     5 int
     6 main(int argc, char **argv)
     7 {
     8     int                    i, maxi, listenfd, connfd, sockfd;
     9     int                    nready;
    10     ssize_t                n;
    11     char                buf[MAXLINE];
    12     socklen_t            clilen;
    13     struct pollfd        client[OPEN_MAX];
    14     struct sockaddr_in    cliaddr, servaddr;
    15 
    16     listenfd = Socket(AF_INET, SOCK_STREAM, 0);
    17 
    18     bzero(&servaddr, sizeof(servaddr));
    19     servaddr.sin_family      = AF_INET;
    20     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    21     servaddr.sin_port        = htons(SERV_PORT);
    22 
    23     Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
    24 
    25     Listen(listenfd, LISTENQ);
    26 
    27     client[0].fd = listenfd;
    28     client[0].events = POLLRDNORM;
    29     // 清空监听描述符数组
    30     for (i = 1; i < OPEN_MAX; i++)
    31         client[i].fd = -1;    
    32     // 该变量表示已连接套接字描述符的最大数量( 曾经 )
    33     maxi = 0;                
    34 
    35     for ( ; ; ) {
    36         nready = Poll(client, maxi+1, INFTIM);
    37 
    38         // 如果有监听描述符检测到可读数据的信号
    39         if (client[0].revents & POLLRDNORM) {    
    40             clilen = sizeof(cliaddr);
    41             connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);
    42 #ifdef    NOTDEF
    43             printf("new client: %s
    ", Sock_ntop((SA *) &cliaddr, clilen));
    44 #endif
    45 
    46             // 登记刚刚获取到的已连接套接字描述符
    47             // OPEN_MAX表示最多监听的描述符个数
    48             for (i = 1; i < OPEN_MAX; i++)
    49                 if (client[i].fd < 0) {
    50                     client[i].fd = connfd;    
    51                     break;
    52                 }
    53             if (i == OPEN_MAX)
    54                 err_quit("too many clients");
    55 
    56             // 设置监听测试事件
    57             client[i].events = POLLRDNORM;
    58             if (i > maxi)
    59                 maxi = i;        
    60 
    61             // 如果信号已经处理完毕则自动进入下一次循环
    62             if (--nready <= 0)
    63                 continue;                
    64         }
    65 
    66         for (i = 1; i <= maxi; i++) {    
    67             if ( (sockfd = client[i].fd) < 0)
    68                 continue;
    69             // 如果检测到可读或者发生错误的信号
    70             if (client[i].revents & (POLLRDNORM | POLLERR)) {
    71                 if ( (n = read(sockfd, buf, MAXLINE)) < 0) {
    72                     if (errno == ECONNRESET) {
    73 #ifdef    NOTDEF
    74                         printf("client[%d] aborted connection
    ", i);
    75 #endif
    76                         Close(sockfd);
    77                         client[i].fd = -1;
    78                     } else
    79                         err_sys("read error");
    80                 } else if (n == 0) {
    81 #ifdef    NOTDEF
    82                     printf("client[%d] closed connection
    ", i);
    83 #endif
    84                     Close(sockfd);
    85                     client[i].fd = -1;
    86                 } else
    87                     Writen(sockfd, buf, n);
    88 
    89                 // 如果检测到可读或者发生错误的信号
    90                 if (--nready <= 0)
    91                     break;            
    92             }
    93         }
    94     }
    95 }

    说明

      如果从可移植性方面考虑或者有处理信号阻塞方面的需求( 要知道select还有个作为其升级版的pselect函数能够妥善处理信号阻塞 ),就还是得用select函数而不是poll函数。否则的话就随意了。

  • 相关阅读:
    UVA 1386 Cellular Automaton
    ZOJ 3331 Process the Tasks
    CodeForces 650B Image Preview
    CodeForces 650A Watchmen
    CodeForces 651B Beautiful Paintings
    CodeForces 651A Joysticks
    HUST 1601 Shepherd
    HUST 1602 Substring
    HUST 1600 Lucky Numbers
    POJ 3991 Seinfeld
  • 原文地址:https://www.cnblogs.com/scut-fm/p/3338185.html
Copyright © 2020-2023  润新知