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、程序测试结果

转载于:https://www.cnblogs.com/alantu2018/p/8469398.html

IO多路复用之poll相关推荐

  1. IO 多路复用之poll总结

    http://www.cnblogs.com/Anker/p/3261006.html IO多路复用之poll总结 1.基本知识 poll的机制与select类似,与select在本质上没有多大差别, ...

  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. opencv算法+人脸检测
  2. 瓜分百万奖金 | 科大讯飞A.I.开发者大赛医疗赛道
  3. listview 滑动以后设置最上面一行为整行展示
  4. gpt最大分区容量_[电脑知识]GUID(GPT)磁盘全局唯一分区表详解
  5. SpringBoot 之 MVC
  6. Python TVTK 标量数据可视化与矢量数据可视化,空间轮廓线可视化
  7. Python之sklearn:LabelEncoder函数简介(编码与编码还原)、使用方法、具体案例之详细攻略
  8. excel在线_怎么能在线免费将PDF转Excel?
  9. 0311互联网新闻 | 知乎增加“视频回答”入口;苹果将最早于今年年底生产AR设备...
  10. Vmware Vsphere虚拟化技术:6.7新特性介绍
  11. java ArrayList排序
  12. unity 编辑器扩展 扩展摄像机的属性查看器
  13. Java并发编程之volatile变量
  14. 《循序渐进Linux(第2版) 基础知识 服务器搭建 系统管理 性能调优 虚拟化与集群应用》——4.7 文本编辑工具vi...
  15. 金蝶K3 WISE 15.0客户端安装部署指南
  16. MediaSession框架全解析
  17. 解决大疆无人机飞了一段距离就停下来的问题(大疆限飞50米)
  18. Torch7系列教程之Torch深度学习库教程(一)
  19. 超强PCB布线设计经验谈附原理图
  20. mysql 大写 小写_MySQL大写和小写问题

热门文章

  1. css3 使用SVG做0.5px 的边框细线
  2. 陶哲轩实分析 习题 13.5.6
  3. 用户控件中动态加入脚本引用
  4. [导入]日志 20071206 (WCF Architecture)
  5. 前端技术周刊 2019-02-26:前端代码保护
  6. cocos2dx JS 游戏切到后台再进入游戏的处理
  7. 原创数据结构算法Flash动画演示课件-Action Script(AS)脚本实现
  8. 在Centos6.5下安装AR8161网卡驱动
  9. iOS8新特性 计算 cell 的高度
  10. Android 快速选择联系人