poll系统调用的原理与原型和select基本类似,也是在指定时间内轮询一定数量的文件描述符,以测试其中是否有就绪者。
poll改进了select模型,其一,不需要在修改传入的参数数组,其二,对于可以监控的个数不再局限于1024,可以通过
cat /proc/sys/fs/file-max
来查看。缺点:
不能指定确定的那个sokect
线程不安全
poll模型的使用
struct pollfd 结构体
struct pollfd { int fd; //指定要监听的文件描述符 short events; //指定监听fd上的什么事件 short revents; //fd上事件就绪后,用于保存实际发生的事件 };
pollfd中的fd是用于记录我们关心的文件描述符;events表示我们关心的事件,如POLLIN、POLLOUT等。revents是实际发生的事件。
通常我们定义这样的结构体数组来存放我们关心的文件描述符,如果有事件发生会改变数组元素的revents值。
函数接口
#include <poll.h> int poll(struct pollfd *fds, nfds_t nfds, int timeout);
参数:
第一个参数是指向一个结构数组的第一个元素的指针,每个元素都是一个pollfd结构,用于指定测试某个给定描述符的条件。
第二个参数是要监听的文件描述符的个数,也就是数组fds的元素个数。
第三个参数意义与select相同。
函数的返回值
成功返回的是有事件的描述符号的个数,出错返回-1,如果返回0,代表等待时间结束,但是没有事件发生。
编码流程
-
定义
pollfd
结构体数组初始化
pollfd
结构体数组设置监听poll事件
等待poll事件
判断触发事件的描述符,并做对应处理。
伪代码
// 定义pollfd结构体数组 struct pollfd pollfds[OPEN_MAX]; // 初始化pollfd结构体数组 int i; for(i=0;i<OPEN_MAX;i++){ pollfds[i].fd = -1; } int pollfds_cnt = 0; // 设置监听事件 pollfds[0].fd = fd1; pollfds[0].event = POLLRDNOM; pollfds_cnt++; pollfds[1].fd = fd1; pollfds[1].event = POLLRDNORM; pollfds_cnt++; // 等待poll事件 if(poll(pollfds,pollfds_cnt,INFTIM)>0) { int i; for(i=0;i<pollfds_cnt;i++) { // 判断触发事件的描述符 if(pollfds[i].fd == fd1 && pollfds[i].revent &POLLRDNORM){ // do something } if(pollfds[i].fd == fd2 && pollfds[i].revent &POLLRDNORM) { // do something } } }
tcp_poll_server.cpp
void show_info(int connfd) { struct sockaddr_in local_addr; bzero(&local_addr,sizeof(local_addr)); socklen_t local_addr_len = sizeof(local_addr); getsockname(connfd,(struct sockaddr*)&local_addr,&local_addr_len); printf("server local %s:%d\n",inet_ntoa(local_addr.sin_addr),ntohs(local_addr.sin_port)); struct sockaddr_in peer_addr; bzero(&peer_addr,sizeof(peer_addr)); socklen_t peer_addr_len = sizeof(peer_addr); getpeername(connfd,(struct sockaddr*)&peer_addr,&peer_addr_len); printf("server peer %s:%d\n",inet_ntoa(peer_addr.sin_addr),ntohs(peer_addr.sin_port)); } int main(int argc,char* argv[]) { if(3 != argc) { printf("usage:%s <ip> <#port>\n",argv[0]); return 1; } int listenfd = socket(AF_INET,SOCK_STREAM,0); if(-1 == listenfd) { perror("listenfd open err"); return 1; } printf("socket create OK\n"); int flag = 1; setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag)); struct sockaddr_in local_addr; bzero(&local_addr,sizeof(local_addr)); local_addr.sin_family = AF_INET; local_addr.sin_addr.s_addr = inet_addr(argv[1]); local_addr.sin_port = htons(atoi(argv[2])); if(-1 == bind(listenfd,(struct sockaddr*)&local_addr,sizeof(local_addr))) { perror("bind err"); return 1; } printf("bind OK\n"); if(-1 == listen(listenfd,10)) { perror("listen err"); return 1; } printf("listen OK\n"); struct pollfd poll_fd[INR_OPEN_MAX]; poll_fd[0].fd = listenfd; poll_fd[0].events = POLLRDNORM; size_t poll_fd_cnt = 1; for(;;) { if(-1 != poll(poll_fd,poll_fd_cnt,-1)) { if(poll_fd[0].revents == POLLRDNORM) { printf("accept listenfd\n"); int connfd = accept(listenfd,NULL,NULL); if(-1 == connfd) { perror("accept err"); } else { if(poll_fd_cnt+1 == INR_OPEN_MAX) { fprintf(stderr,"connfd size over %d",INR_OPEN_MAX); close(connfd); } else { poll_fd[poll_fd_cnt].fd = connfd; poll_fd[poll_fd_cnt].events = POLLRDNORM; poll_fd_cnt++; } } } int i; //下标从1开始,0存放的是监听套结字 for(i=1; i<poll_fd_cnt; i++) { if(poll_fd[i].revents & POLLRDNORM) { char buf[BUFSIZ]; bzero(buf,BUFSIZ); ssize_t len; printf("read connfd %d\n",poll_fd[i].fd); if((len = read(poll_fd[i].fd,buf,BUFSIZ-1)) == -1) { perror("read err"); //return 1; } if(0 == len) { printf("close %d\n",poll_fd[i].fd); printf("%d vs %d\n",poll_fd[i].revents,poll_fd[i].revents); close(poll_fd[i].fd); printf("%d vs %d\n",poll_fd[i].revents,poll_fd[i].revents); memcpy(poll_fd+i,poll_fd+i+1,poll_fd_cnt-i-1); poll_fd_cnt--; i--;//数组发生变化,重新判断i的fd continue; //break; } printf("server recv:%s\n",buf); int fd = open(buf,O_RDONLY); if(-1 == fd) { perror("open file err"); } struct stat file_stat; fstat(fd,&file_stat); if(-1 == sendfile(poll_fd[i].fd,fd,NULL,file_stat.st_size)) { perror("sendfile err"); } printf("server send file %s ok\n",buf); close(fd); } } } } close(listenfd); }
-