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标志的一些常值。

上图需要注意的是,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(一个负数值),表示永远等待,即一直阻塞。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! \n");
            }
            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]='\0';
                    printf("Socket %d said : %s\n",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复用有更深入的认识。

unix下网络编程之I/O复用(三)的更多相关文章

  1. unix下网络编程之I/O复用(一)

    什么是I/O复用? What we need is the capability to tell the kernel that we want to be notified if one or mo ...

  2. unix下网络编程之I/O复用(五)

    前言 本章节是用基本的Linux/Unix基本函数加上select调用编写一个完整的服务器和客户端例子,可在Linux(ubuntu)和Unix(freebsd)上运行,客户端和服务端的功能如下: 客 ...

  3. unix下网络编程之I/O复用(二)

    select函数 该函数允许进程指示内核等待多个事件中的任何一个发生,并仅在有一个或是多个事件发生或经历一段指定的时间后才唤醒它.我们调用select告知内核对哪些描述字(就读.写或异常条件)感兴趣以 ...

  4. unix下网络编程之I/O复用(四)

    首先需要了解的是select函数: select函数 #include<sys/select.h> #include<sys/time.h> int select (int m ...

  5. TCP/IP网络编程之I/O复用

    基于I/O复用的服务端 在前面章节的学习中,我们看到了当有新的客户端请求时,服务端进程会创建一个子进程,用于处理和客户端的连接和处理客户端的请求.这是一种并发处理客户端请求的方案,但并不是一个很好的方 ...

  6. python3网络编程之socketserver

    本节主要是讲解python3网络编程之socketserver,在上一节中我们讲到了socket.由于socket无法支持多用户和多并发,于是就有了socket server. socket serv ...

  7. GO语言的进阶之路-网络编程之socket

    GO语言的进阶之路-网络编程之socket 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是socket; 在说socket之前,我们要对两个概念要有所了解,就是IP和端口 ...

  8. 网络编程之socket

    网络编程之socket socket:在网络编程中的一个基本组件,也称套接字. 一个套接字就是socket模块中的socket类的一个实例. 套接字包括两个: 服务器套接字和客户机套接字 套接字的实例 ...

  9. java网络编程之TCP通讯

    java中的网络编程之TCP协议的详细介绍,以及如何使用,同时我在下面举2例说明如何搭配IO流进行操作, /* *TCP *建立连接,形成传输数据的通道: *在连接中进行大数据量传输: *通过三次握手 ...

随机推荐

  1. mysql下merge分表

    1.merge简介分表就是把N条记录的表,分成若干个分表,各个分表记录的总和仍为N. 分表的方法有很多,用merge来分表,是最简单的一种方式.merge是mysql的一种存储引擎,它把一组MyISA ...

  2. [RK3288][Android6.0] TS-ADC驱动流程小结【转】

    本文转载自:https://blog.csdn.net/kris_fei/article/details/55045936 Platform: RK3288OS: Android 6.0Kernel: ...

  3. Python 条件判断语句(if ,elif, else)

    条件判断可以分: 单分支判断:只有一个if语句 双分支判断:if else 的格式 多分支判断:if elif  else 的格式 条件语句嵌套判断 # 下面是个条件多分支判断 score = 85 ...

  4. shell运行java/Jar 脚本

    1.Shell执行/调用Java/Jar程序 #!/bin/bash JAVA_HOME="$HOME/jdk" BASE_DIR=`dirname $0` if [ " ...

  5. RPC与HTTP

    一.为什么需要RPC,而不是简单的HTTP接口? RPC(即Remote Procedure Call,远程过程调用),主要是基于TCP/IP协议:而HTTP服务主要是基于HTTP协议的.我们都知道H ...

  6. centos下无法使用lsof命令"-bash: lsof: command not found"

    1.问题描述 : 在CentOS下,使用lsof命令,报错如下: 2.解决方法: #yum install lsof 若输入y不能安装成功,通过yum install 包 -y 进行安装: # yum ...

  7. ElasticSearch入门常用命令

    基于开源项目MyAlice智能客服学习ElasticSearch https://github.com/hpgary/MyAlice/wiki/%E7%AC%AC01%E7%AB%A0%E5%AE%8 ...

  8. 直播P2P技术3-伙伴节点质量评估及子流订阅

    以上模型,暂且称之为W-P2P吧.

  9. 如何用Qt写一个同一时间只能运行一个实例的应用程序

    http://blog.sina.com.cn/s/blog_6343941a0100nk2x.html 可以达到的目的: 1.应用只启动一个实例,依赖于QtNetwork模块 2.启动时向另一个实例 ...

  10. nginx基本参数详解

    运行用户 user nobody; 启动进程,通常设置成和cpu的数量相等 worker_processes 1; 全局错误日志及PID文件 error_log logs/error.log; err ...