poll数组
1、poll()函数:服务器
这个函数是某些Unix系统提供的用于执行与select()函数同等功能的函数,自认为poll和select大同小异,下面是这个函数的声明:
socket
#include <poll.h> int poll(struct pollfd *fds, nfds_t nfds, int timeout);
参数:函数
1.第一个参数:一个结构数组,struct pollfd:测试
fds:是一个struct pollfd结构类型的数组,每一个数组元素都是一个pollfd结构,用于指定测试某个给定描述字fd的条件。存放须要检测其状态的Socket描述符;每当调用这个函数以后,系统不会清空这个数组,操做起来比较方便;特别是对于socket链接比较多的状况下,在必定程度上能够提升处理的效率;这一点与select()函数不一样,调用select()函数以后,select()函数会清空它所检测的socket描述符集合,致使每次调用select()以前都必须把socket描述符从新加入到待检测的集合中;所以,select()函数适合于只检测一个socket描述符的状况,而poll()函数适合于大量socket描述符的状况;
spa
结构以下:code
struct pollfd{ int fd; //文件描述符 short events; //请求的事件 short revents; //返回的事件 };
events和revents是经过对表明各类事件的标志进行逻辑或运算构建而成的。events包括要监视的事件(就是我须要关注的时间,是读?是写?仍是出错?),poll用已经发生的事件填充revents。poll函数经过在revents中设置标志肌肤POLLHUP、POLLERR和POLLNVAL来反映相关条件的存在。不须要在events中对于这些标志符相关的比特位进行设置。若是fd小于0, 则events字段被忽略,而revents被置为0.标准中没有说明如何处理文件结束。文件结束能够经过revents的标识符POLLHUN或返回0字节的常规读操做来传达。即便POLLIN或POLLRDNORM指出还有数据要读,POLLHUP也可能会被设置。所以,应该在错误检验以前处理正常的读操做。blog
poll函数的事件标志符值:接口
常量 | 说明 |
POLLIN | 普通或优先级带数据可读 |
POLLRDNORM | 普通数据可读 |
POLLRDBAND | 优先级带数据可读 |
POLLPRI | 高优先级数据可读 |
POLLOUT | 普通数据可写 |
POLLWRNORM | 普通数据可写 |
POLLWRBAND | 优先级带数据可写 |
POLLERR | 发生错误 |
POLLHUP | 发生挂起 |
POLLNVAL | 描述字不是一个打开的文件 |
注意:进程
1)后三个只能做为描述字的返回结果存储在revents中,而不能做为测试条件用于events中。
2)第二个参数nfds:要监视的描述符的数目。
3)最后一个参数timeout:是一个用毫秒表示的时间,是指定poll在返回前没有接收事件时应该等待的时间。若是 它的值为-1,poll就永远都不会超时。若是整数值为32个比特,那么最大的超时周期大约是30分钟。
timeout值 | 说明 |
INFTIM | 永远等待 |
0 | 当即返回,不阻塞进程 |
>0 | 等待指定数目的毫秒数 |
对socket描述符fd上的读、写、异常事件感兴趣,就能够这样作:
struct pollfd fds; fds[index].events=POLLIN | POLLOUT | POLLERR;
struct pollfd fds; //检测可读TCP链接请求: if((fds[nIndex].revents & POLLIN) == POLLIN) { <span style="white-space:pre"> </span>//接收数据,调用accept()接收链接请求 } //检测可写: if((fds[nIndex].revents & POLLOUT) == POLLOUT) { <span style="white-space:pre"> </span>//发送数据 } //检测异常: if((fds[nIndex].revents & POLLERR) == POLLERR) { <span style="white-space:pre"> </span>//异常处理 }2、实例TCP服务器的服务器程序
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <netdb.h> #include <string.h> #include <errno.h> #include <poll.h> //for poll #define LISTENQ 1024 #define MAXLINE 1024 #define OPEN_MAX 50000 #ifndef INFTIM #define INFTIM -1 #endif int start_up(char* ip,int port) //建立一个套接字,绑定,检测服务器 { //sock //1.建立套接字 int sock=socket(AF_INET,SOCK_STREAM,0); if(sock<0) { perror("sock"); exit(0); } //2.填充本地 sockaddr_in 结构体(设置本地的IP地址和端口) struct sockaddr_in local; local.sin_port=htons(port); local.sin_family=AF_INET; local.sin_addr.s_addr=inet_addr(ip); //3.bind()绑定 if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0) { perror("bind"); exit(1); } //4.listen()监听 检测服务器 if(listen(sock,back_log)<0) { perror("sock"); exit(1); } return sock; //这样的套接字返回 } int main(int argc, char *argv[]) { int i, maxi, connfd, sockfd; int nready; ssize_t n; socklen_t clilen; struct sockaddr_in servaddr; socklen_t len=sizeof(servaddr); char buf[BUFSIZ]; struct pollfd client[OPEN_MAX]; // 用于poll函数第一个参数的数组,存放每次的文件描述符个数 if( argc != 3 ) { printf("Please input %s <hostname>\n", argv[0]); exit(2); } int listenfd=start_up(argv[1],argv[2]); //建立一个绑定了本地 ip 和端口号的套接字描述符 client[0].fd = listenfd; //将数组中的第一个元素设置成监听描述字 client[0].events = POLLIN; //将测试条件设置成普通或优先级带数据可读(感兴趣的事件读、写、出错),此处书中为POLLRDNORM,*/ client[0].revents = 0; //真正发生的事件 for(i = 1;i < OPEN_MAX; ++i) //数组中的其它元素将暂时设置成不可用 { client[i].fd = -1; } maxi = 0; while(1) { nready = poll(client, maxi+1,INFTIM); //将进程阻塞在poll上 if( client[0].revents & POLLIN) //先测试监听描述字 { connfd = accept(listenfd,(struct sockaddr*)&servaddr, &clilen); for(i = 1; i < OPEN_MAX; ++i) { if( client[i].fd < 0 ) { client[i].fd = connfd; //将新链接加入到测试数组中 client[i].events = POLLIN; //POLLRDNORM; 测试条件普通数据可读 break; } if( i == OPEN_MAX ) { printf("too many clients"); //链接的客户端太多了,都达到最大值了 exit(1); } if( i > maxi ) maxi = i; //maxi记录的是数组元素的个数 if( --nready <= 0 ) continue; //若是没有可读的描述符了,就从新监听链接 } } for(i = 1; i <= maxi; i++) //测试除监听描述字之后的其它链接描述字 { if( (sockfd = client[i].fd) < 0) //若是当前描述字不可用,就测试下一个 continue; if(client[i].revents & (POLLIN | POLLERR))//若是当前描述字返回的是普通数据可读或出错条件 { if( (n = read(sockfd, buf, MAXLINE)) < 0) //从套接口中读数据 { if( errno == ECONNRESET) //若是链接断开,就关闭链接,并设当前描述符不可用 { close(sockfd); client[i].fd = -1; } else perror("read error"); } else if(n == 0) //若是数据读取完毕,关闭链接,设置当前描述符不可用 { close(sockfd); client[i].fd = -1; } else write(sockfd, buf, n); //打印数据 if(--nready <= 0) break; } } } exit(0); }赐教!