I/O复用----poll

一、使用
int poll(struct pollfd *fds,int nfds,int timeout);

(1) struct pollfd
{
int fd; //关注的文件描述符
short events; // 关注的是文件描述符上的哪种事件
short revents; // 由内核修改的,返回此文件描述符上发生的事件类型(必须是events指定的关注的事件)

(2)nfds:数组的长度

(3)timeout: 超时时间 -1 代表阻塞,直到有事件发生

I/O复用----poll

二、代码

#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <poll.h>

#define FDMAXNUM 100

void InitFds(struct pollfd *fds)
{
	int i = 0;
	for(; i < FDMAXNUM; ++i)
	{
		fds[i].fd = -1;
	}
}

void DeleteFd(int fd, struct pollfd *fds)
{
	int i = 0;
	for(; i < FDMAXNUM; ++i)
	{
		if(fds[i].fd == fd)
		{
			fds[i].fd = -1;
			break;
		}
	}
}

void AddFd(int fd, struct pollfd *fds)
{
	int i = 0;
	for(; i < FDMAXNUM; ++i)
	{
		if(fds[i].fd == -1)
		{
			fds[i].fd = fd;
			fds[i].events = POLLIN | POLLRDHUP;
			break;
		}
	}
}

void GetClientLink(int fd, struct pollfd *fds)
{
	struct sockaddr_in cli;
	int len = sizeof(cli);

	int c = accept(fd, (struct sockaddr*)&cli, &len);
	if(c  == -1)
	{
		return;
	}

	AddFd(c, fds);
}



void DealClientData(int fd,  struct pollfd *fds, int rdhup)
{
	if(rdhup) //断开链接
	{
		close(fd);
		DeleteFd(fd, fds);
		return;
	}

	char buff[128] = {0};
	int n = recv(fd, buff, 127, 0);
	if(n <= 0)
	{
		DeleteFd(fd, fds);
		return;
	}

	printf("%s\n", buff);

	send(fd, "OK", 2, 0);
}

int main()
{
	int listenfd = socket(AF_INET, SOCK_STREAM, 0);
	assert(listenfd != -1);

	struct sockaddr_in ser;
	memset(&ser, 0, sizeof(ser));
	ser.sin_family = AF_INET;
	ser.sin_port = htons(6000);
	ser.sin_addr.s_addr = inet_addr("127.0.0.1");

	int res = bind(listenfd, (struct sockaddr*)&ser, sizeof(ser));
	assert(res != -1);

	listen(listenfd, 5);

	struct pollfd  fds[FDMAXNUM];
	InitFds(fds);
	fds[0].fd = listenfd;
	fds[0].events = POLLIN;

	while(1)
	{
		int n = poll(fds, FDMAXNUM, -1);
		if(n <= 0)
		{
			exit(0);
		}

		int i = 0;
		for(; i < FDMAXNUM; ++i)
		{
			if(fds[i].fd == -1)
			{
				continue;
			}
			
			if(fds[i].fd == listenfd && fds[i].revents & POLLIN)
			{
				GetClientLink(fds[i].fd, fds);
			}
			else if(fds[i].revents & POLLIN)
			{
				if(fds[i].revents & POLLRDHUP)
					DealClientData(fds[i].fd, fds, 1);
				else
					DealClientData(fds[i].fd, fds, 0);
			}
		}
	}
}

三、优点

1、将用户关注的事件与内核修改的就绪事件分割开,每次调用poll不需要重新设置
2、poll通过int类型记录文件描述符,文件描述符的取值范围扩大到系统最大限制 65535
3、用户关注的所有文件描述符通过fds指向的用户数组来记录,则可以关注的文件描述符由用户自己指定,能扩大到系统最大限制

四、缺点
1、返回值返回就绪的文件描述符个数, poll也仅仅是在fds指向的数组元素中标记出哪个文件描述符就绪
用户探测就绪文件描述符的时间复杂度依旧是O(n)
2、内核依旧采用轮询方式处理

上一篇:select/epoll模型


下一篇:使用读写管道作为事件通知的C++实现