• 不为客户连接创建子进程的并发回射服务器( 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函数。否则的话就随意了。

  • 相关阅读:
    php获取http请求原文
    windows下安装MongoDB服务
    微服务RESTful 接口设计规范
    mysql主从复制原理及步骤
    nodejs主要框架
    redis事务机制和分布式锁
    计算机专业学生一定要学好这几门课
    redis常见7种使用场景
    js类型判断
    SQL语句:case when then的用法
  • 原文地址:https://www.cnblogs.com/scut-fm/p/3338185.html
Copyright © 2020-2023  润新知