----------------------------------------------------------------------
#include <poll.h>
int poll(struct pollfd *fdarray, unsigned long nfds, int timeout);
返回:准备好描述字的个数,0—超时,-1—出错。
----------------------------------------------------------------------
参数说明:
fdarray: 是一个pollfd结构类型的指针,指向一个链表,pollfd结构由三部分组成:文件描述符以及要检查的条件和检查后返回的结果;
nfds: 要检查的文件描述符的个数,也就是以上链表中元素的个数;
timeout: 超时时间,单位为毫秒,若为INFTIM则表示永远等待,若为0表示立即返回。
pollfd为一个结构体:
1 struct pollfd
2 {
3     int fd; /* descriptor to check */
4     short events; /* events of interest on fd */
5     short revents; /* events that occurred on fd */
6 };
一下是events和revents可能的值:
| 常量 | 能作为events的输入吗? | 能作为revents的结果吗? | 解释 | 
| POLLIN | yes | yes | 普通或优先级波段数据可读 | 
| POLLRDNORM | yes | yes | 普通数据可读 | 
| POLLRDBAND | yes | yes | 优先级波段数据可读 | 
| POLLPRI | yes | 
 | 高优先级数据可读 | 
| POLLOUT | yes | yes | 普通或优先级波段数据可写 | 
| POLLWRNORM | yes | yes | 普通数据可写 | 
| POLLWRBAND | yes | yes | 优先级波段数据可写 | 
| POLLERR | 
 | yes | 发生错误 | 
| POLLHUP | 
 | yes | 发生挂起 | 
| POLLNVAL | 
 | yes | 描述字不是一个打开的文件 | 
上图可分为三部分:四个处理输入的常量;三个处理输出的常量;三个处理错误的常量。
poll识别三个类别的数据:普通(normal)、优先级波段(priority band)、高优先级(high priority)。术语来自流的概念。
poll接口返回说明:
所有正规TCP数据和UDP数据都被认为是普通数据;
TCP的带外数据被认为是优先级带数据;
当TCP连接的读这一半关闭时(如接收了一个FIN),这也认为是普通数据,且后续的读操作将返回0;
TCP连接存在错误既可以认为是普通数据,也可以认为是错误(POLLERR)。无论哪种情况,后续的读操作将返回-1,并将errno置为适当的值,这就处理了诸如接收到RST或超时等条件;
在监听套接口上新连接的可用性既可认为是普通数据,也可以认为是优先级带数据,大多数实现都将其作为普通数据考虑。
如果不关心某个特定的描述字,可将其pollfd结构的fd成员置为一个负值,这样就可以忽略成员events,且返回时将成员revents的值置为0。
poll没有select存在的最大描述字数目问题。但可移植性select要好于poll。
  1 #include <stdio.h>
  2 #include <sys/socket.h>
  3 #include <netinet/in.h>
  4 #include <strings.h>
  5 #include <poll.h>
  6 
  7 #define PORT 8080
  8 #define LISTENQ 5
  9 #define MAXLINE 1024
 10 #define OPEN_MAX 1024
 11 
 12 #define IS_ERROR(condition)  13     if(condition)  14     {  15         printf("Error in func[%s] and line[%d]!\n",  16             __PRETTY_FUNCTION__, __LINE__);  17         return 0;  18     }
 19 
 20 #ifndef INFTIM
 21 #define INFTIM (-1)
 22 #endif
 23 
 24 int main(int argc, char *argv[])
 25 {
 26     struct sockaddr_in addrSer;
 27     struct sockaddr_in addrCli;
 28     int listenSock;
 29     int connSock;
 30     struct pollfd clientSock[OPEN_MAX];
 31 
 32     int sumSock;     //sum of client sockets - 1
 33     int nCliLen;     //len of addrCli
 34     int nReady;      //the num of ready sockets
 35     char buf[MAXLINE];
 36     int nRet;
 37     int i;
 38 
 39     /*create listen socket*/
 40     listenSock = socket(AF_INET, SOCK_STREAM, 0);
 41     IS_ERROR(listenSock == -1);
 42 
 43     /*bind listen port*/
 44     bzero(&addrSer, sizeof(addrSer));
 45     addrSer.sin_family      = AF_INET;
 46     addrSer.sin_addr.s_addr = htonl(INADDR_ANY);
 47     addrSer.sin_port        = htons(PORT);
 48     nRet = bind(
 49         listenSock,
 50         (struct sockaddr *)&addrSer,
 51         sizeof(struct sockaddr_in)
 52     );
 53     IS_ERROR(nRet == -1);
 54 
 55     /*listen port*/
 56     nRet = listen(listenSock, LISTENQ);
 57     IS_ERROR(nRet == -1);
 58 
 59     /*init*/
 60     clientSock[0].fd = listenSock;
 61     clientSock[0].events = POLLRDNORM;
 62     for (i=1; i<OPEN_MAX; ++i)
 63     {
 64         clientSock[i].fd = -1;
 65     }
 66 
 67     sumSock = 0;
 68 
 69     /*request*/
 70     while (1)
 71     {
 72         nReady = poll(clientSock, sumSock+1, INFTIM);
 73 
 74         /*accept*/
 75         if (clientSock[0].revents & POLLRDNORM)
 76         {
 77             nCliLen = sizeof(addrCli);
 78             connSock = accept(clientSock[0].fd, (struct sockaddr *)&addrCli, &nCliLen);
 79 
 80             for (i=1; i<OPEN_MAX; ++i)
 81             {
 82                 if (clientSock[i].fd < 0)
 83                 {
 84                     clientSock[i].fd = connSock;
 85                     break;
 86                 }
 87             }
 88 
 89             if (i == OPEN_MAX)
 90             {
 91                 printf("too many clients!\n");
 92                 return 0;
 93             }
 94 
 95             clientSock[i].events = POLLRDNORM;
 96             sumSock = (sumSock < i) ? i : sumSock;
 97 
 98             if (--nReady <= 0)
 99             {
100                 continue;
101             }
102         }
103 
104         /*send and recv*/
105         for (i=1; i<=sumSock; ++i)
106         {
107             if (clientSock[i].fd < 0)
108             {
109                 continue;
110             }
111 
112             if (clientSock[i].revents & (POLLRDNORM | POLLERR))
113             {
114                 nRet = recv(clientSock[i].fd, buf, MAXLINE, 0);
115 
116                 if (nRet == 0 || nRet == -1)
117                 {
118                     printf("read sock %d err, nRet = %d!\n", clientSock[i], nRet);
119                     close(clientSock[i].fd);
120                     clientSock[i].fd = -1;
121                 }
122                 else if (-1 == send(clientSock[i].fd, buf, nRet, 0))
123                 {
124                     printf("write sock %d err!\n", clientSock[i]);
125                     close(clientSock[i].fd);
126                     clientSock[i].fd = -1;
127                 }
128 
129                 if (--nReady <= 0)
130                 {
131                     break;
132                 }
133             } //if (clientSock[i].revents & (POLLRDNORM | POLLERR))
134         } //for (i=1; i<=sumSock; ++i)
135     } //while (1)
136 
137     return 0;
138 }
和select()不一样,poll()没有使用低效的三个基于位的文件描述符set,而是采用了一个单独的结构体pollfd数组,由一个指针指向这个组。
poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程跟select一样,经历了多次无谓的遍历。
poll比select好的一点是,没有最大连接数限制,原因是它是基于链表来存储的,而select则会有最大描述符的限制。
每一个pollfd结构体指定了一个被监视的文件描述符,可以传递多个结构体,指示poll()监视多个文件描述符。每个结构体的events域是监视该文件描述符的事件掩码,由用户来设置这个域。revents域是文件描述符的操作结果事件掩码。内核在调用返回时设置这个域。events域中请求的任何事件都可能在revents域中返回。
events和revents两个域可设置的事件掩码在1.2中都有介绍。
POLLIN | POLLPRI等价于select()的读事件,POLLOUT | POLLWRBAND等价于select()的写事件。POLLIN等价于POLLRDNORM | POLLRDBAND,而POLLOUT则等价于POLLWRNORM。
例如,要同时监视一个文件描述符是否可读和可写,我们可以设置events为POLLIN | POLLOUT。在poll返回时,我们可以检查revents中的标志,对应于文件描述符请求的events结构体。如果POLLIN事件被设置,则文件描述符可以被读取而不阻塞。如果POLLOUT被设置,则文件描述符可以写入而不导致阻塞。这些标志并不是互斥的:它们可能被同时设置,表示这个文件描述符的读取和写入操作都会正常返回而不阻塞。
原文:http://www.cnblogs.com/mhscn/p/4741087.html