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结构体定义如下:

?
123456struct 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程序,功能是客户端向服务器发送信息,服务器接收输出并原样发送回给客户端,客户端接收到输出到终端。

服务器端程序如下:

?
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h> #include <netinet/in.h>#include <sys/socket.h>#include <poll.h>#include <unistd.h>#include <sys/types.h> #define ipaddress  "127.0.0.1"#define port    8787#define maxline   1024#define listenq   5#define open_max  1000#define inftim   -1 //函数声明//创建套接字并进行绑定static int socket_bind(const char* ip,int port);//io多路复用pollstatic void do_poll(int listenfd);//处理多个连接static void handle_connection(struct pollfd *connfds,int num); int main(int argc,char *argv[]){int listenfd,connfd,sockfd;struct sockaddr_in cliaddr;socklen_t cliaddrlen;listenfd = socket_bind(ipaddress,port);listen(listenfd,listenq);do_poll(listenfd);return 0;} static int socket_bind(const char* ip,int port){int listenfd;struct sockaddr_in servaddr;listenfd = socket(af_inet,sock_stream,0);if (listenfd == -1){perror("socket error:");exit(1);}bzero(&servaddr,sizeof(servaddr));servaddr.sin_family = af_inet;inet_pton(af_inet,ip,&servaddr.sin_addr);servaddr.sin_port = htons(port);if (bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) == -1){perror("bind error: ");exit(1);}return listenfd;} static void do_poll(int listenfd){int connfd,sockfd;struct sockaddr_in cliaddr;socklen_t cliaddrlen;struct pollfd clientfds[open_max];int maxi;int i;int nready;//添加监听描述符clientfds[0].fd = listenfd;clientfds[0].events = pollin;//初始化客户连接描述符for (i = 1;i < open_max;i++)clientfds[i].fd = -1;maxi = 0;//循环处理for ( ; ; ){//获取可用描述符的个数nready = poll(clientfds,maxi+1,inftim);if (nready == -1){perror("poll error:");exit(1);}//测试监听描述符是否准备好if (clientfds[0].revents & pollin){cliaddrlen = sizeof(cliaddr);//接受新的连接if ((connfd = accept(listenfd,(struct sockaddr*)&cliaddr,&cliaddrlen)) == -1){if (errno == eintr)continue;else{perror("accept error:");exit(1);}}fprintf(stdout,"accept a new client: %s:%d\n", inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port);//将新的连接描述符添加到数组中for (i = 1;i < open_max;i++){if (clientfds[i].fd < 0){clientfds[i].fd = connfd;break;}}if (i == open_max){fprintf(stderr,"too many clients.\n");exit(1);}//将新的描述符添加到读描述符集合中clientfds[i].events = pollin;//记录客户连接套接字的个数maxi = (i > maxi ? i : maxi);if (--nready <= 0)continue;}//处理客户连接handle_connection(clientfds,maxi);}} static void handle_connection(struct pollfd *connfds,int num){int i,n;char buf[maxline];memset(buf,0,maxline);for (i = 1;i <= num;i++){if (connfds[i].fd < 0)continue;//测试客户描述符是否准备好if (connfds[i].revents & pollin){//接收客户端发送的信息n = read(connfds[i].fd,buf,maxline);if (n == 0){close(connfds[i].fd);connfds[i].fd = -1;continue;}// printf("read msg is: ");write(stdout_fileno,buf,n);//向客户端发送bufwrite(connfds[i].fd,buf,n);}}}

客户端代码如下所示:

?
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071#include <netinet/in.h>#include <sys/socket.h>#include <stdio.h>#include <string.h>#include <stdlib.h>#include <poll.h>#include <time.h>#include <unistd.h>#include <sys/types.h> #define maxline   1024#define ipaddress  "127.0.0.1"#define serv_port  8787 #define max(a,b) (a > b) ? a : b static void handle_connection(int sockfd); int main(int argc,char *argv[]){int         sockfd;struct sockaddr_in servaddr;sockfd = socket(af_inet,sock_stream,0);bzero(&servaddr,sizeof(servaddr));servaddr.sin_family = af_inet;servaddr.sin_port = htons(serv_port);inet_pton(af_inet,ipaddress,&servaddr.sin_addr);connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));//处理连接描述符handle_connection(sockfd);return 0;} static void handle_connection(int sockfd){char  sendline[maxline],recvline[maxline];int   maxfdp,stdineof;struct pollfd pfds[2];int n;//添加连接描述符pfds[0].fd = sockfd;pfds[0].events = pollin;//添加标准输入描述符pfds[1].fd = stdin_fileno;pfds[1].events = pollin;for (; ;){poll(pfds,2,-1);if (pfds[0].revents & pollin){n = read(sockfd,recvline,maxline);if (n == 0){fprintf(stderr,"client: server is closed.\n");close(sockfd);}write(stdout_fileno,recvline,n);}//测试标准输入是否准备好if (pfds[1].revents & pollin){n = read(stdin_fileno,sendline,maxline);if (n == 0){shutdown(sockfd,shut_wr);continue;}write(sockfd,sendline,n);}}}

4、程序测试结果

以上就是小编为大家带来的io多路复用之poll全面总结(必看篇)全部内容了,希望大家多多支持~

服务器安全狗主动防御之文件及目录保护功能教程

一.文件及目录保护作用服务器安全狗文件及目录保护功能,其作用主要是为了保护用户服务器重要的文件与目录不被篡改与删除。建议用户安装完服务器安全狗后,要注意开启文件及目录保护功能以便保障文件与目录的安全。二.文件及目录保护设置文件及目录保护规则类型主要分为系统保护...
服务器安全狗服务器安全主动防御目录保护

2020年黑客首选10大Windows网络攻击技术

Red Canary近期公布了《2021 Threat Detection Report》,该报告涵盖了众多顶级网络攻击技术到MITER ATT&CK框架的映射。其中,就2020年黑客首选10大Windows网络攻击技术进行了调研。1、24%:命令行解...
Windows服务器安全网络攻击黑客

网站防护可以采用高防CDN吗?

网站防护采用高防CDN怎么样?网站防护可以采用高防CDN。高防CDN接入云防御,智能防护,隐藏源站IP,防止黑客获取源站真实IP,保护网站远离DDOS攻击。确保加速性能的前提下全面提升网站安全性。网站防护采用高防CDN有以下几个优势:1.稳定快速有效防御DDo...
服务器安全高防cdn网站防护

在Debian上设置USB网络打印机和扫描仪服务器的技巧

假定你想要在你的家庭/办公网络中设置一台Linux打印服务器,而你手头上却只有USB打印机可用(因为他们比那些有着内建网络接口或无线模块的打印机要便宜得多)。此外,如果这些设备中有一台是一体化的,而你也想要通过网络共享其整合的扫描仪,这该怎么办?在本文中,我将...
ubuntu服务器Debian网络打印机扫描仪USB