http://www.cnblogs.com/Anker/p/3261006.html

IO多路复用之poll总结

1、基本知识

  poll的机制与select类似,与select在本质上没有多大差别,管理多个描述符也是进行轮询,根据描述符的状态进行处理,但是poll没有最大文件描述符数量的限制。poll和select同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。

2、poll函数

  函数格式如下所示:

# include <poll.h>
int poll ( struct pollfd * fds, unsigned int nfds, int timeout);

pollfd结构体定义如下:

struct pollfd {

int fd;         /* 文件描述符 */
short events;         /* 等待的事件 */
short revents;       /* 实际发生了的事件 */
} ;

  每一个pollfd结构体指定了一个被监视的文件描述符,可以传递多个结构体,指示poll()监视多个文件描述符。每个结构体的events域是监视该文件描述符的事件掩码,由用户来设置这个域。revents域是文件描述符的操作结果事件掩码,内核在调用返回时设置这个域。events域中请求的任何事件都可能在revents域中返回。合法的事件如下:

  POLLIN         有数据可读。

  POLLRDNORM       有普通数据可读。

  POLLRDBAND      有优先数据可读。

  POLLPRI         有紧迫数据可读。

  POLLOUT            写数据不会导致阻塞。

  POLLWRNORM       写普通数据不会导致阻塞。

  POLLWRBAND        写优先数据不会导致阻塞。

  POLLMSGSIGPOLL     消息可用。

  此外,revents域中还可能返回下列事件:
  POLLER     指定的文件描述符发生错误。

  POLLHUP   指定的文件描述符挂起事件。

  POLLNVAL  指定的文件描述符非法。

这些事件在events域中无意义,因为它们在合适的时候总是会从revents中返回。

  使用poll()和select()不一样,你不需要显式地请求异常情况报告。
  POLLIN | POLLPRI等价于select()的读事件,POLLOUT |POLLWRBAND等价于select()的写事件。POLLIN等价于POLLRDNORM |POLLRDBAND,而POLLOUT则等价于POLLWRNORM。例如,要同时监视一个文件描述符是否可读和可写,我们可以设置 events为POLLIN |POLLOUT。在poll返回时,我们可以检查revents中的标志,对应于文件描述符请求的events结构体。如果POLLIN事件被设置,则文件描述符可以被读取而不阻塞。如果POLLOUT被设置,则文件描述符可以写入而不导致阻塞。这些标志并不是互斥的:它们可能被同时设置,表示这个文件描述符的读取和写入操作都会正常返回而不阻塞。

  timeout参数指定等待的毫秒数,无论I/O是否准备好,poll都会返回。timeout指定为负数值表示无限超时,使poll()一直挂起直到一个指定事件发生;timeout为0指示poll调用立即返回并列出准备好I/O的文件描述符,但并不等待其它的事件。这种情况下,poll()就像它的名字那样,一旦选举出来,立即返回。

  返回值和错误代码
  成功时,poll()返回结构体中revents域不为0的文件描述符个数;如果在超时前没有任何事件发生,poll()返回0;失败时,poll()返回-1,并设置errno为下列值之一:
  EBADF         一个或多个结构体中指定的文件描述符无效。

  EFAULTfds   指针指向的地址超出进程的地址空间。

  EINTR      请求的事件之前产生一个信号,调用可以重新发起。

  EINVALnfds  参数超出PLIMIT_NOFILE值。

  ENOMEM       可用内存不足,无法完成请求。

3、测出程序

  编写一个echo server程序,功能是客户端向服务器发送信息,服务器接收输出并原样发送回给客户端,客户端接收到输出到终端。

  服务器端程序如下:

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 #include <errno.h>
  5
  6 #include <netinet/in.h>
  7 #include <sys/socket.h>
  8 #include <poll.h>
  9 #include <unistd.h>
 10 #include <sys/types.h>
 11
 12 #define IPADDRESS   "127.0.0.1"
 13 #define PORT        8787
 14 #define MAXLINE     1024
 15 #define LISTENQ     5
 16 #define OPEN_MAX    1000
 17 #define INFTIM      -1
 18
 19 //函数声明
 20 //创建套接字并进行绑定
 21 static int socket_bind(const char* ip,int port);
 22 //IO多路复用poll
 23 static void do_poll(int listenfd);
 24 //处理多个连接
 25 static void handle_connection(struct pollfd *connfds,int num);
 26
 27 int main(int argc,char *argv[])
 28 {
 29     int  listenfd,connfd,sockfd;
 30     struct sockaddr_in cliaddr;
 31     socklen_t cliaddrlen;
 32     listenfd = socket_bind(IPADDRESS,PORT);
 33     listen(listenfd,LISTENQ);
 34     do_poll(listenfd);
 35     return 0;
 36 }
 37
 38 static int socket_bind(const char* ip,int port)
 39 {
 40     int  listenfd;
 41     struct sockaddr_in servaddr;
 42     listenfd = socket(AF_INET,SOCK_STREAM,0);
 43     if (listenfd == -1)
 44     {
 45         perror("socket error:");
 46         exit(1);
 47     }
 48     bzero(&servaddr,sizeof(servaddr));
 49     servaddr.sin_family = AF_INET;
 50     inet_pton(AF_INET,ip,&servaddr.sin_addr);
 51     servaddr.sin_port = htons(port);
 52     if (bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) == -1)
 53     {
 54         perror("bind error: ");
 55         exit(1);
 56     }
 57     return listenfd;
 58 }
 59
 60 static void do_poll(int listenfd)
 61 {
 62     int  connfd,sockfd;
 63     struct sockaddr_in cliaddr;
 64     socklen_t cliaddrlen;
 65     struct pollfd clientfds[OPEN_MAX];
 66     int maxi;
 67     int i;
 68     int nready;
 69     //添加监听描述符
 70     clientfds[0].fd = listenfd;
 71     clientfds[0].events = POLLIN;
 72     //初始化客户连接描述符
 73     for (i = 1;i < OPEN_MAX;i++)
 74         clientfds[i].fd = -1;
 75     maxi = 0;
 76     //循环处理
 77     for ( ; ; )
 78     {
 79         //获取可用描述符的个数
 80         nready = poll(clientfds,maxi+1,INFTIM);
 81         if (nready == -1)
 82         {
 83             perror("poll error:");
 84             exit(1);
 85         }
 86         //测试监听描述符是否准备好
 87         if (clientfds[0].revents & POLLIN)
 88         {
 89             cliaddrlen = sizeof(cliaddr);
 90             //接受新的连接
 91             if ((connfd = accept(listenfd,(struct sockaddr*)&cliaddr,&cliaddrlen)) == -1)
 92             {
 93                 if (errno == EINTR)
 94                     continue;
 95                 else
 96                 {
 97                    perror("accept error:");
 98                    exit(1);
 99                 }
100             }
101             fprintf(stdout,"accept a new client: %s:%d\n", inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port);
102             //将新的连接描述符添加到数组中
103             for (i = 1;i < OPEN_MAX;i++)
104             {
105                 if (clientfds[i].fd < 0)
106                 {
107                     clientfds[i].fd = connfd;
108                     break;
109                 }
110             }
111             if (i == OPEN_MAX)
112             {
113                 fprintf(stderr,"too many clients.\n");
114                 exit(1);
115             }
116             //将新的描述符添加到读描述符集合中
117             clientfds[i].events = POLLIN;
118             //记录客户连接套接字的个数
119             maxi = (i > maxi ? i : maxi);
120             if (--nready <= 0)
121                 continue;
122         }
123         //处理客户连接
124         handle_connection(clientfds,maxi);
125     }
126 }
127
128 static void handle_connection(struct pollfd *connfds,int num)
129 {
130     int i,n;
131     char buf[MAXLINE];
132     memset(buf,0,MAXLINE);
133     for (i = 1;i <= num;i++)
134     {
135         if (connfds[i].fd < 0)
136             continue;
137         //测试客户描述符是否准备好
138         if (connfds[i].revents & POLLIN)
139         {
140             //接收客户端发送的信息
141             n = read(connfds[i].fd,buf,MAXLINE);
142             if (n == 0)
143             {
144                 close(connfds[i].fd);
145                 connfds[i].fd = -1;
146                 continue;
147             }
148            // printf("read msg is: ");
149             write(STDOUT_FILENO,buf,n);
150             //向客户端发送buf
151             write(connfds[i].fd,buf,n);
152         }
153     }
154 }

客户端代码如下所示:

 1 #include <netinet/in.h>
 2 #include <sys/socket.h>
 3 #include <stdio.h>
 4 #include <string.h>
 5 #include <stdlib.h>
 6 #include <poll.h>
 7 #include <time.h>
 8 #include <unistd.h>
 9 #include <sys/types.h>
10
11 #define MAXLINE     1024
12 #define IPADDRESS   "127.0.0.1"
13 #define SERV_PORT   8787
14
15 #define max(a,b) (a > b) ? a : b
16
17 static void handle_connection(int sockfd);
18
19 int main(int argc,char *argv[])
20 {
21     int                 sockfd;
22     struct sockaddr_in  servaddr;
23     sockfd = socket(AF_INET,SOCK_STREAM,0);
24     bzero(&servaddr,sizeof(servaddr));
25     servaddr.sin_family = AF_INET;
26     servaddr.sin_port = htons(SERV_PORT);
27     inet_pton(AF_INET,IPADDRESS,&servaddr.sin_addr);
28     connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
29     //处理连接描述符
30     handle_connection(sockfd);
31     return 0;
32 }
33
34 static void handle_connection(int sockfd)
35 {
36     char    sendline[MAXLINE],recvline[MAXLINE];
37     int     maxfdp,stdineof;
38     struct pollfd pfds[2];
39     int n;
40     //添加连接描述符
41     pfds[0].fd = sockfd;
42     pfds[0].events = POLLIN;
43     //添加标准输入描述符
44     pfds[1].fd = STDIN_FILENO;
45     pfds[1].events = POLLIN;
46     for (; ;)
47     {
48         poll(pfds,2,-1);
49         if (pfds[0].revents & POLLIN)
50         {
51             n = read(sockfd,recvline,MAXLINE);
52             if (n == 0)
53             {
54                     fprintf(stderr,"client: server is closed.\n");
55                     close(sockfd);
56             }
57             write(STDOUT_FILENO,recvline,n);
58         }
59         //测试标准输入是否准备好
60         if (pfds[1].revents & POLLIN)
61         {
62             n = read(STDIN_FILENO,sendline,MAXLINE);
63             if (n  == 0)
64             {
65                 shutdown(sockfd,SHUT_WR);
66         continue;
67             }
68             write(sockfd,sendline,n);
69         }
70     }
71 }

4、程序测试结果

5、参考资料

http://blog.endlesscode.com/2010/03/27/select-poll-epoll-intro/

http://hi.baidu.com/xzf20082004/item/622fb01a1018c7f5746a846f

IO 多路复用之poll总结相关推荐

  1. IO多路复用之poll

    1.基本知识 poll的机制与select类似,与select在本质上没有多大差别,管理多个描述符也是进行轮询,根据描述符的状态进行处理,但是poll没有最大文件描述符数量的限制.poll和selec ...

  2. python3 异步 非阻塞 IO多路复用 select poll epoll 使用

    有许多封装好的异步非阻塞IO多路复用框架,底层在linux基于最新的epoll实现,为了更好的使用,了解其底层原理还是有必要的. 下面记录下分别基于Select/Poll/Epoll的echo ser ...

  3. IO多路复用select/poll/epoll详解以及在Python中的应用

    IO multiplexing(IO多路复用) IO多路复用,有些地方称之为event driven IO(事件驱动IO). 它的好处在于单个进程可以处理多个网络IO请求.select/epoll这两 ...

  4. IO多路复用之poll总结

    http://www.cnblogs.com/Anker/p/3261006.html 1.基本知识 poll的机制与select类似,与select在本质上没有多大差别,管理多个描述符也是进行轮询, ...

  5. Python异步非阻塞IO多路复用Select/Poll/Epoll使用

    来源:http://www.haiyun.me/archives/1056.html 有许多封装好的异步非阻塞IO多路复用框架,底层在linux基于最新的epoll实现,为了更好的使用,了解其底层原理 ...

  6. IO多路复用select,poll epoll以及区别

    看这个一次读懂 Select.Poll.Epoll IO复用技术 文章来简单理解下,如果不是很明白的话,可以参考下面转的知乎上面白话文列子 作者:Leslie 链接:https://www.zhihu ...

  7. python poll_python IO 多路复用 select poll epoll

    select select 原理 select 是通过系统调用来监视着一个由多个文件描述符(file descriptor)组成的数组,当select()返回后,数组中就绪的文件描述符会被内核修改标记 ...

  8. 【Linux系统编程】IO多路复用之poll

    00. 目录 文章目录 00. 目录 01. 概述 02. poll函数 03. 程序示例 04. poll优缺点 05. 附录 01. 概述 select() 和 poll() 系统调用的本质一样, ...

  9. IO多路复用原理剖析

    (最近笔试遇到笔试题:select,poll,epoll都是IO多路复用的机制). I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应 ...

最新文章

  1. c#编译器对byte类型的一些规则 (转)
  2. 【C++ 语言】面向对象 ( 继承 | 重写 | 子类调用父类方法 | 静态多态 | 动态多态 | 虚函数 | 纯虚函数 )
  3. jQuery 内容文本值|| 案例:购物车案例模块-增减商品数量 || 案例:购物车案例模块-修改商品小计
  4. go语言基础到提高(9)-go ide
  5. Runtime Error VS Accepted (大整数排序 )
  6. 打印基于某category创建的所有product
  7. 在嵌套使用if语句时,C语言规定else总是什么?
  8. u盘安装linux 提示no such device_Linux 网络基础设计
  9. Linux | 进程概念、进程状态(僵尸进程、孤儿进程、守护进程)、进程地址空间
  10. linux 内置ssh,Linux ssh内置sftp配置说明
  11. 信息学奥赛一本通C++语言——1110:查找特定的值
  12. Hands-on Lab (14) - 部署StatefulSets应用
  13. 漫画:什么是A*寻路算法?
  14. php 识别图片指定区域,简单实现图片局部识别的方法
  15. python writelines_详解详解Python中writelines()方法的使用
  16. AIS数据-渔船的货船的区分(Distinction between the cargo ships of fishing boats)
  17. 谷歌浏览器不能上网问题
  18. php mip,php批量清除MIP缓存
  19. 下载安装Vue-CLI
  20. win7笔记本网络连接图标一直转圈但可上网

热门文章

  1. python基础:迭代器、生成器(yield)详细解读
  2. c/c++编码规范(2)--作用域
  3. 程序开发基础学习四(boost::signal2 函数学习)
  4. 通过OracleDataReader来读取BLOB类型的数据 (转载)
  5. mysql表单查询_MySQL表单集合查询
  6. asp服务器组件安装包,ASP服务器组件
  7. python编写自定义函数判断n1-n2范围内的素数_【每日道代码题001】- PYTHON基础复习...
  8. 搬运机器人举杯贺所需的条件_智能搬运机器人比赛规则
  9. boost原理与sklearn源码_机器学习sklearn系列之决策树
  10. OJ RuntimeError常见原因