码迷,mamicode.com
首页 > 其他好文 > 详细

UNIX网络编程-Poll模型学习

时间:2015-08-19 01:55:00      阅读:143      评论:0      收藏:0      [点我收藏+]

标签:

1、相关接口介绍

1.1 poll

----------------------------------------------------------------------

#include <poll.h>

int poll(struct pollfd *fdarray, unsigned long nfds, int timeout);

返回:准备好描述字的个数,0—超时,-1—出错。

----------------------------------------------------------------------

参数说明:

fdarray: 是一个pollfd结构类型的指针,指向一个链表,pollfd结构由三部分组成:文件描述符以及要检查的条件和检查后返回的结果;

nfds: 要检查的文件描述符的个数,也就是以上链表中元素的个数;

timeout: 超时时间,单位为毫秒,若为INFTIM则表示永远等待,若为0表示立即返回。

1.2 pollfd

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。

2、poll的工作流程跟select差不多,这里直接跳过,来看相应的demo

  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 }

3、poll和select对比

3.1 poll和select的优缺点

和select()不一样,poll()没有使用低效的三个基于位的文件描述符set,而是采用了一个单独的结构体pollfd数组,由一个指针指向这个组。

poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程跟select一样,经历了多次无谓的遍历。

poll比select好的一点是,没有最大连接数限制,原因是它是基于链表来存储的,而select则会有最大描述符的限制。

3.2 poll和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被设置,则文件描述符可以写入而不导致阻塞。这些标志并不是互斥的:它们可能被同时设置,表示这个文件描述符的读取和写入操作都会正常返回而不阻塞。

 

UNIX网络编程-Poll模型学习

标签:

原文地址:http://www.cnblogs.com/mhscn/p/4741087.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!