• unix下网络编程之I/O复用(三)***


    poll函数

        在上文unix下网络编程之I/O复用(二)中已经介绍了select函数的相关使用,本文将介绍另一个常用的I/O复用函数poll。poll提供的功能与select类似,不过在处理流设备时,它能够提供额外的信息。

    poll函数原型:

    1
    2
    3
    #include<poll.h>
       int poll (struct pollfd * fdarray , unsigned long nfds , int timeout);
       //返回:就需描述字的个数,0——超时,-1——出错

     第一个参数是指向一个结构数组第一个元素的指针,每个数组元素都是一个pollfd结构。如下:

    1
    2
    3
    4
    5
    struct pollfd {
        int fd; //descriptor to check
        short events; //events of interest on fd
    `   short revents; //events tha occurred on fd
    }

    要测试的条件由events成员指定,函数在相应的revents成语中返回该描述字的状态。(每个描述字都有两个变量,一个为调用值,另一个为返回结果,从而避免使用值-结果参数,这与select函数是不同的)。下图列出了用于指定events标志以及测试revents标志的一些常值。

    image

    上图需要注意的是,POLLERR,POLLHUP,POLLNVAL是处理错误的描述字,因此它们也就不可以出现在input事件中,即events。poll识别三类数据:普通(normal),优先级带(priority band)和高优先级(high priority)。

    对TCP和UPD而言,以下条件引起poll返回特定的revents。

    1、 All regular TCP data and all UDP data is considered normal. 
    2、 TCP's out-of-band data (Chapter 24) is considered priority band. 
    3、 When the read half of a TCP connection is closed (e.g., a FIN is received), this is also considered normal data and a subsequent read operation will return 0. 
    4、 The presence of an error for a TCP connection can be considered either normal data or an error (POLLERR). In either case, a subsequent read will return –1 with errno set to the appropriate value. This handles conditions such as the receipt of an RST or a timeout. 
    5、 The availability of a new connection on a listening socket can be considered either normal data or priority data. Most implementations consider this normal data. 
    6、 The completion of a nonblocking connect is considered to make a socket writable.

                                                                                        ——《unix网络编程》第三版

    参数nfds,指示结构数组中元素的个数。

    参数timeout:

    与select中的timeout不同,poll函数的timeout参数是一int值,表示poll函数返回前等待多长时间,它是毫秒级别的。它有三种情况的取值:

      1、INFTIM(一个负数值 -1),表示永远等待,即一直阻塞,-1:一直阻塞到fds数组中有一个达到就绪态或者捕获到一个信号。

      2、0,表示立即返回,非阻塞。

      3、>0,表示正待指定数目的毫秒数。

    poll函数的返回值:

    当poll发生错误时,poll函数的返回值-1,若定时器时间到之前没有任何描述字就绪,则返回0,否则返回就绪描述字的个数,即其revents成员值非0的描述字个数。

    如果我们不再关心某个特定描述字,那么可以把与他对应的pollfd结构的fd成员设置成一个负值。poll函数将忽略这样的pollfd结构的events成员,返回时将它的revents成员的值置为0。

    poll函数的通信列子:一个简单的TCP回射服务器程序

    pollServer.c:使用select机制的服务器程序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    #include <stdio.h>
    #include <string.h>
    #include <arpa/inet.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <poll.h>
     
    /*环境为ubuntu10.04自带c环境,无法自动引入下列宏,所以自己写在前面了*/
    #define INFTIM -1
    #define POLLRDNORM  0x040       /* Normal data may be read.  */
    #define POLLRDBAND  0x080       /* Priority data may be read.  */
    #define POLLWRNORM  0x100       /* Writing now will not block.  */
    #define POLLWRBAND  0x200       /* Priority data may be written.  */
     
    #define MAXLINE  1024
    #define OPEN_MAX  16 //一些系统会定义这些宏
    #define SERV_PORT  10001
     
    int main()
    {
        int i , maxi ,listenfd , connfd , sockfd ;
        int nready;
        int n;
        char buf[MAXLINE];
        socklen_t clilen;
        struct pollfd client[OPEN_MAX];
     
        struct sockaddr_in cliaddr , servaddr;
        listenfd = socket(AF_INET , SOCK_STREAM , 0);
        memset(&servaddr,0,sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(SERV_PORT);
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
     
        bind(listenfd , (struct sockaddr *) & servaddr, sizeof(servaddr));
        listen(listenfd,10);
        client[0].fd = listenfd;
        client[0].events = POLLRDNORM;
        for(i=1;i<OPEN_MAX;i++)
        {
            client[i].fd = -1;
        }
        maxi = 0;
     
        for(;;)
        {
            nready = poll(client,maxi+1,INFTIM);
            if (client[0].revents & POLLRDNORM)
            {
                clilen = sizeof(cliaddr);
                connfd = accept(listenfd , (struct sockaddr *)&cliaddr, &clilen);
                for(i=1;i<OPEN_MAX;i++)
                {
                    if(client[i].fd<0)
                    {
                        client[i].fd = connfd;
                        client[i].events = POLLRDNORM;
                        break;
                    }
                }
                if(i==OPEN_MAX)
                {
                    printf("too many clients! ");
                }
                if(i>maxi) maxi = i;
                nready--;
                if(nready<=0) continue;
            }
     
            for(i=1;i<=maxi;i++)
            {
                if(client[i].fd<0) continue;
                sockfd = client[i].fd;
                if(client[i].revents & (POLLRDNORM|POLLERR))
                {
                    n = read(client[i].fd,buf,MAXLINE);
                    if(n<=0)
                    {
                        close(client[i].fd);
                        client[i].fd = -1;
                    }
                    else
                    {
                        buf[n]='';
                        printf("Socket %d said : %s ",sockfd,buf);
                        write(sockfd,buf,n); //Write back to client
                    }
                    nready--;
                    if(nready<=0) break; //no more readable descriptors
                }
            }
        }
        return 0;
    }

    客户端程序参考上一篇文章。

    总结:

       本文介绍了poll函数的原型,参数说明,注意事项以及一个简单的代码例子。在unix后续版本中,加入了epoll函数I/O复用机制,它在一定条件下更加高效,在以后的文章中,会对epoll机制再进行详细的描述。之前在学习python的时候,也接触了select和poll,但是当时了解的比较浅显,希望通过最近的学习可以对unix下I/O复用有更深入的认识。

  • 相关阅读:
    耿建超英语语法---使动词
    tensorboard的安装及遇到的问题
    利用PIL实现图片的切割
    mysql explain
    laravel sql查询
    聚簇索引和非聚簇索引
    修改数据表结构导致的问题
    接口优化记录
    redis优化记录
    php 判断两个数组是否相等
  • 原文地址:https://www.cnblogs.com/jiangzhaowei/p/8972168.html
Copyright © 2020-2023  润新知