Linux高性能服务器——I/O复用之poll

34 篇文章 0 订阅
15 篇文章 0 订阅

 I/O复用可同时监听多个文件描述符发生事件,很大程度提高了程序的性能。I/O复用适用的场合一般有如下几点:

  • 客户端程序要同时处理多个socket;
  • 客户端程序要同时处理用户输入和网络连接。如聊天室程序;
  • TCP服务器同时监听socket和连接socket。如服务器连接负载均衡;
  • 服务器要同时处理TCP请求和UDP请求。如回射服务器;
  • 服务器需要同时监听多个端口,或者处理多种服务。如xinetd服务器;

        I/O复用本身是阻塞的,即没有文件描述符有事件发生就为阻塞状态。并且当多个文件描述符同时就绪时,如果不采取其他措施,I/O复用也只能按顺序对一个一个文件描述符进行处理,即服务器是串行工作的。可以采取多进程或多线程的方式来实现并发。Linix下I/O复用的系统调用主要是select、poll和epoll,以下是介绍poll系统调用的简单使用。

 

1. poll 相关API

poll系统调用和select类似,也是在用户指定的时间内轮询一定数量的文件描述符,以检测是否有就绪的文件描述符,原型如下:

#include<poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
  • 参数
  1. fds参数是一个pollfd结构体数组,它指定所有用户感兴趣的文件描述符上的可读、可写和异常事件。pollfd结构体定义如下:
    struct pollfd
    {
        int fd;  /* 文件描述符 */
        short events;  /* 注册的事件 */
        short revents;  /* 实际发生的事件,由内核填充 */
    };
    其中,events成员告诉poll所要监听fd上的哪些事件,它是一系列事件的按位或;revents成员则由内核修改,以反馈给应用程序fd上实际发生了哪些事件。poll支持的事件类型有以下几种:
  2. nfds参数指定被监听事件集合fds的大小。其类型nfds_t的定义如下:
    typedef unsigned long int nfds_t;

     

  3. timeout参数指定poll的超时值,单位为毫秒。设置为-1时,poll调用一直阻塞,直到有事件发生。设置为0立即返回。返回值与select相同。

 

2. poll的使用

#define SIZE 5   /* 最大监听文件描述符的个数 */

void Init_fd(struct pollfd *fds)   /* 初始化pollfd结构体数组 */
{
	for(int i = 0;; i < SIZE; ++i)
	{
		fds[i].fd = -1;
		fds[i].events = 0;
		fds[i].revents = 0;
	}
}

void Insert_fd(struct pollfd *fds, int fd, short event)  /* 添加文件描述符及关注的事件 */
{
	for(int i = 0;; i < SIZE; ++i)
	{
		if(-1 == fds[i].fd)
		{
			fds[i].fd = fd;
			fds[i].events = event;
			break;
		}
	}
}

void Delete_fd(struct pollfd *fds, int fd)  /* 清除文件描述符 */
{
	int i = 0;
	for(; i < SIZE; ++i)
	{
		if(fds[i].fd == fd)
		{
			fds[i].fd = -1;
			fds[i].events = 0;
			break;
		}
	}
}

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

	struct sockaddr_in ser,cli;
	ser.sin_family = AF_INET;
	ser.sin_port = htons(6000);
	ser.sin_addr.s_addr = inet_addr("127.0.0.1");
	
	int res = bind(sockfd, (struct sockaddr *)&ser, sizeof(ser));
	assert(res != -1);

	res = listen(sockfd, 10);

	struct pollfd fds[SIZE];   /* pollfd结构体数组 */
	Init_fd(fds);   /* 初始化fds */
		
	Insert_fd(fds, sockfd, POLLIN);   /* 添加sockfd套接字 */
	
	while(1)
	{
		int n = poll(fds, SIZE, -1);  /* 调用poll系统调用 */

		if(n <= 0)
		{
			printf("poll error!\n");
			continue;
		}
	
		int i = 0;
		for(; i < SIZE; ++i)
		{
			int fd = fds[i].fd;
			
			if(fds[i].fd != -1)
			{
				if(fds[i].revents & POLLRDHUP)  /* 对方关闭连接 */
				{
					close(fd);
					Delete_fd(fds, fd);
				}
				else if(fds[i].revents & POLLIN)   /* 可读事件发生 */
				{
					if(fd == sockfd)   /* 有新的连接请求 */
					{
						printf("linked one client!\n");
						int len = sizeof(cli);
						int c = accept(sockfd, (struct sockaddr *)&cli, &len);
						if(c < 0)
						{
							printf("accept error!\n");
							continue;
						}
	
						Insert_fd(fds, c, POLLIN | POLLRDHUP);

					}
					else  /* 接受数据 */
					{
						char buff[128] = {0};
						int n = recv(fd, buff, 127, 0);
						if(n <= 0)
						{
							printf("recv error!\n");
							continue;
						}
						printf("%d %s\n",fd, buff);
						send(fd, "OK", 2, 0);
					}
				}
			}	
		}
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值