函数说明
函数原型
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
函数功能: 等待一个文件描述符转变为就绪状态
参数1:
第一个参数是指向一个结构数组第一个元素的指针。每个数组元素都是一个pollfd
结构,用于指定测试某个给定描述符fd的条件。
struct pollfd {
int fd; /* 文件描述符 */
short events; /* 监控的事件 */
short revents; /* 监控事件中满足条件返回的事件 */
};
图6-23列出了用于指定events
标志以及测试revents
标志的一些常值。
poll识别三类数据:普通( normal)、优先级带(priority band)和高优先级(high priority)。
这些术语均出自基于流的实现。
POLLIN
可被定义为POLLRDNORM
和POLLRDBAND
的逻辑或。POLLIN
自SVR3实现就存在,早于SVR4中的优先级带,为了向后兼容,该常值继续保留。类似地,POLLOUT
等 同于POLLWRNORM
,前者早于后者。
参数2:
监控数组中有多少文件描述符需要被监控
参数3:
timeout 毫秒级等待
- -1:阻塞等,#define INFTIM -1 (Linux中没有定义此宏)
- 0:立即返回,不阻塞进程
- 大于0:等待指定毫秒数
如果系统不能提供亳秒级精度的定时器,该值就向上舍入到最接近的支持值。
返回值:
当发生错误时,poll
函数的返回值为-1,若定时器到时之前没有任何描述符就绪,则返回0,否则返回就绪描述符的个数,即结构数组的revents
成员值非0的描述符个数。
注意:如果不再监控某个文件描述符时,可以把pollfd
中,fd设置为-1,poll
不再监控此pollfd
,下次poll
返回时,会把revents
设置为0。
在select
中会有FD_SETSIZE
的限制,而有了poll就不再有那样的问题了,因为分配一个pollfd
结构的数组并把该数组中元素的数目通知内核成了调用者的责任。内核不再需要知道类似fa_set
的固定大小的数据类型。
回射服务器程序实现
/* server.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <poll.h>
#include <errno.h>
#include "wrap.h"
#define MAXLINE 80
#define SERV_PORT 6666
#define OPEN_MAX 1024
int main(int argc, char *argv[])
{
int i, j, maxi, listenfd, connfd, sockfd;
int nready;
ssize_t n;
char buf[MAXLINE], str[INET_ADDRSTRLEN];
socklen_t clilen;
struct pollfd client[OPEN_MAX];
struct sockaddr_in cliaddr, servaddr;
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
Listen(listenfd, 20);
client[0].fd = listenfd;
client[0].events = POLLRDNORM; /* listenfd监听普通读事件 */
for (i = 1; i < OPEN_MAX; i++)
client[i].fd = -1; /* 用-1初始化client[]里剩下元素 */
maxi = 0; /* client[]数组有效元素中最大元素下标 */
for ( ; ; ) {
nready = poll(client, maxi+1, -1); /* 阻塞 */
if (client[0].revents & POLLRDNORM) { /* 有客户端链接请求 */
clilen = sizeof(cliaddr);
connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
printf("received from %s at PORT %d\n",
inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
ntohs(cliaddr.sin_port));
for (i = 1; i < OPEN_MAX; i++) {
if (client[i].fd < 0) {
client[i].fd = connfd; /* 找到client[]中空闲的位置,存放accept返回的connfd */
break;
}
}
if (i == OPEN_MAX)
perr_exit("too many clients");
client[i].events = POLLRDNORM; /* 设置刚刚返回的connfd,监控读事件 */
if (i > maxi)
maxi = i; /* 更新client[]中最大元素下标 */
if (--nready <= 0)
continue; /* 没有更多就绪事件时,继续回到poll阻塞 */
}
for (i = 1; i <= maxi; i++) { /* 检测client[] */
if ((sockfd = client[i].fd) < 0)
continue;
if (client[i].revents & (POLLRDNORM | POLLERR)) {
if ((n = Read(sockfd, buf, MAXLINE)) < 0) {
if (errno == ECONNRESET) { /* 当收到 RST标志时 */
/* connection reset by client */
printf("client[%d] aborted connection\n", i);
Close(sockfd);
client[i].fd = -1;
} else {
perr_exit("read error");
}
} else if (n == 0) {
/* connection closed by client */
printf("client[%d] closed connection\n", i);
Close(sockfd);
client[i].fd = -1;
} else {
for (j = 0; j < n; j++)
buf[j] = toupper(buf[j]);
Writen(sockfd, buf, n);
}
if (--nready <= 0)
break; /* no more readable descriptors */
}
}
}
return 0;
}