IO复用之poll函数介绍

文章目录

    • 函数说明
    • 回射服务器程序实现


函数说明

函数原型

#include 
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标志的一些常值。
IO复用之poll函数介绍_第1张图片
poll识别三类数据:普通( normal)、优先级带(priority band)和高优先级(high priority)。
这些术语均出自基于流的实现。
POLLIN可被定义为POLLRDNORMPOLLRDBAND的逻辑或。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 
#include 
#include 
#include 
#include 
#include 
#include 
#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;
}

上篇:IO复用之select函数介绍
下篇:IO复用之epoll_*函数介绍

你可能感兴趣的:(网络编程)