poll的实现和select非常相似,只是描述fd集合的方式不同,poll使用pollfd结构而不是select的fd_set结构,其他的都差不多。
poll #include <poll.h> int poll( struct pollfd *fds, nfds_t nfds, int timeout );//第一个参数,传入的是一个结构体数组。
struct pollfd { int fd; /* 文件描述符*/ short events; /* 监控的事件*/ short revents; /* 监控事件中满足条件返回的事件*/ };
//监控事件的类型 POLLIN普通或带外优先数据可读,即POLLRDNORM | POLLRDBAND POLLRDNORM-数据可读 POLLRDBAND-优先级带数据可读 POLLPRI 高优先级可读数据 POLLOUT普通或带外数据可写 POLLWRNORM-数据可写 POLLWRBAND-优先级带数据可写 POLLERR 发生错误 POLLHUP 发生挂起 POLLNVAL 描述字不是一个打开的文件
nfds 监控数组中有多少文件描述符需要被监控 timeout 毫秒级等待 -1:阻塞等,#define INFTIM -1 Linux中没有定义此宏 0:立即返回,不阻塞进程 >0:等待指定毫秒数,如当前系统时间精度不够毫秒,向上取值 如果不再监控某个文件描述符时,可以把pollfd中,fd设置为-1,poll不再监控此 pollfd,下次返回时,把revents设置为0。 ppoll GNU定义了ppoll(非POSIX标准),可以支持设置信号屏蔽字,大家可参考poll模型自行实现C/S
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <ctype.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/time.h> #include <arpa/inet.h> #include <netinet/in.h> #include <poll.h> #include <errno.h> #define SERVER_PORT 8000 #define LISTEN_LEN 128 #define BUF_SIZE 1500 #define FD_MAX 1024 //select 最大限制1024 int main(int argc , char * argv[]) { struct sockaddr_in serveraddr,clientaddr; int maxfd,flag,ready;//maxfd打开的最大文件描述符,flag是client数组中有内容的最大下标,ready存放有事件发生的个数 int listenfd,clientfd,tempfd; int cliaddr_len,len; int i,j; char ipstr[128],buf[BUF_SIZE]; struct pollfd cliARR[FD_MAX]; /*创建socket*/ listenfd = socket(AF_INET,SOCK_STREAM,0); /*初始化bzero*/ bzero(&serveraddr,sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); serveraddr.sin_port = htons(SERVER_PORT); /*绑定bind()*/ bind(listenfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr)); listen(listenfd,LISTEN_LEN); cliARR[0].fd = listenfd; cliARR[0].events = POLLRDNORM;//listen监听事件 for(i = 1;i<FD_MAX;i++) cliARR[i].fd=-1; flag = 0; for( ; ; ){ ready = poll(cliARR,flag+1,-1); /*阻塞*/ if(cliARR[0].revents & POLLRDNORM){ //判断listenfd是否有数据到达 cliaddr_len = sizeof(clientaddr); clientfd = accept(listenfd,(struct sockaddr*)&clientaddr,&cliaddr_len); printf("client IP:%s\tPORT:%d\n", inet_ntop(AF_INET, &clientaddr.sin_addr, ipstr, sizeof(ipstr)), ntohs(clientaddr.sin_port)); for(i = 1;i<FD_MAX;i++) if(cliARR[i].fd<0){ cliARR[i].fd = clientfd; break; } if(i == FD_MAX){ printf("too many cliens\n"); exit(-1); } cliARR[i].events = POLLRDNORM; if(i>flag) flag = i; if(--ready == 0) continue; } //接收普通socket的数据 for(i=1;i<=flag;i++){ if((tempfd = cliARR[i].fd)<0) continue; if(cliARR[i].revents&(POLLRDNORM | POLLERR)){ if((len = read(tempfd,buf,sizeof(buf)))<0){ if(errno == ECONNRESET){ close(tempfd); cliARR[i].fd=-1; }else{ perror("read ERROR"); exit(-1); } }else if(len == 0){ close(tempfd); cliARR[i].fd=-1; }else{ for(j = 0;j<len;j++) buf[j]=toupper(buf[j]); write(tempfd,buf,len); } if(--ready == 0) break; } } } close(listenfd); return 0; }