poll相关的api:
// 把所有的文件描述符放到pollfd数组里面,每调用一次poll,需要对所有的pollfd数组做一次遍历。 // "fds"参数是struct pollfd数组 extern int poll (struct pollfd *__fds, nfds_t __nfds, int __timeout); /* pollfd封装了感兴趣的事件和实际发生的事件 */ /* Data structure describing a polling request. */ struct pollfd { int fd; /* File descriptor to poll. */ short int events; /* Types of events poller cares about. */ short int revents; /* Types of events that actually occurred. */ }; /* Type used for the number of file descriptors. */ typedef unsigned long int nfds_t;
网上搜到的代码示例:
#include <stdio.h> #include <fcntl.h> #include <netinet/in.h> #include <poll.h> #include <errno.h> #define MAXLINE 512 #define OPEN_MAX 1024 #define SA struct sockaddr int main() { int listenfd, connfd, sockfd, i, maxi; int nready; socklen_t clilen; ssize_t n; char buf[MAXLINE]; //我们关心的文件描述符 struct pollfd client[OPEN_MAX]; struct sockaddr_in servaddr, cliaddr; //创建监听套接字 if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("socket() error!"); exit(0); } //对协议地址进行清零 bzero(&servaddr, sizeof(servaddr)); //设置为 IPv4 or IPv6 servaddr.sin_family = AF_INET; //绑定本地端口号 servaddr.sin_port = htons(9999); //任何一个 IP 地址,让内核自行选择 servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //绑定套接口到本地协议地址 if (bind(listenfd, (SA *) &servaddr, sizeof(servaddr)) < 0) { printf("bind() error!"); exit(0); } //服务器开始监听 if (listen(listenfd, 5) < 0) { printf("listen() error!"); exit(0); } //监听套接字 client[0].fd = listenfd; client[0].events = POLLRDNORM; //读事件 for (i = 1; i < OPEN_MAX; ++i) { client[i].fd = -1; } maxi = 0; for (;;) { //poll函数的返回值:发生了事件的文件描述符的个数, 它并没有指出哪几个描述符发生了事件. //timeout参数为-1,poll阻塞直到描述符有事件发生 nready = poll(client, maxi + 1, -1); //首先检查监听套接字,查看是否有新连接进来 if (client[0].revents & POLLRDNORM) { clilen = sizeof(cliaddr); connfd = accept(listenfd, (SA *) &cliaddr, &clilen); if (connfd < 0) { continue; } for (i = 1; i < OPEN_MAX; ++i) { if (client[i].fd < 0) { client[i].fd = connfd; //printf does not work fprintf(stdout, "accept a new client "); break; } } if (i == OPEN_MAX) { fprintf(stdout, "too many clients"); exit(0); } client[i].events = POLLRDNORM; if (i > maxi) { maxi = i; } if (--nready <= 0) continue; } //然后检查连接套接字 for (i = 1; i < OPEN_MAX; ++i) { if ((sockfd = client[i].fd) < 0) { continue; } if (client[i].revents & POLLRDNORM | POLLERR) { if ((n = read(sockfd, buf, MAXLINE)) < 0) { if (errno == ECONNRESET) { close(sockfd); printf("1.close fd %d! ", client[i].fd); client[i].fd = -1; } else { printf("read error! "); } } else if (n == 0) { close(sockfd); printf("2.close fd %d! ", client[i].fd); client[i].fd = -1; } else { write(sockfd, buf, n); } if (--nready <= 0) break; } } } }
使用nc命令作为客户端进行交互:nc localhost 9999
从这个例子中,能看出poll的缺点。
poll函数返回发生了事件的描述符个数,我们需要遍历所有的描述符,才能知道具体描述符哪些真实发生了事件。