I/O复用——poll

poll

#include <poll.h>

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

系统调用成功返回就绪文件描述符的总数,超时返回0,失败返回-1

参数含义
fdsstruct polled结构类型的数组,它指定所有用户感兴趣的文件描述符上发生的可读、可写和异常等事件
nfds指定被监听事件集合fds的大小
timeout指定poll的超时值,单位是毫秒, timeout为-1时,poll调用将永久阻塞,直到某个事件发生;为0时,poll调用将立即返回
pollfd结构
struct pollfd
{
	int fd;  // 用户设置关注的文件描述符
	short events;  // 用户关注的事件类型
	short revents;  // 由内核填充就绪的事件类型
};

poll支持的事件类型:
在这里插入图片描述

poll和select的异同
  • poll将用户关注的事件类型和就绪的事件类型分隔开
  • poll可以关注多个事件类型,并且观测的文件描述符没有数量的限制,文件描述符的值也不再限制在1024以内
  • 对于两者,用户探测就绪事件的时间复杂度仍为O(n)

使用poll方法实现一个简单的TCP服务器端

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#include <poll.h>

#define FDS_LENGTH 100  // 数组长度


// 创建并初始化连接套接字sockfd
int InitSocket()
{
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd == -1)
	{
		return -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(sockfd, (struct sockaddr*)&ser, sizeof(ser));
	if (res == -1)
	{
		return -1;
	}

	res = listen(sockfd, 5);
	if (res == -1)
	{
		return -1;
	}

	return sockfd;
}

// 初始化fds数组
void InitFds(struct pollfd *fds)
{
	int i = 0;
	for (; i < FDS_LENGTH; ++i)
	{
		fds[i].fd = -1;
		fds[i].events = 0;
	}
}

// 向数组中添加新的文件描述符
void InsertFd(struct pollfd *fds, int fd, short events)
{
	int i = 0;
	for (; i < FDS_LENGTH; ++i)
	{
		if (fds[i].fd == -1)
		{
			fds[i].fd = fd;
			fds[i].events = events;
			break;
		}
	}
}

// 处理就绪事件
void DealFinishEvent(struct pollfd *fds, int sockfd)
{
	int i = 0;
	for (; i < FDS_LENGTH; ++i)
	{
		if (fds[i].fd == -1) // 无效的文件描述符
		{
			continue;
		}
		else if (fds[i].fd == sockfd) // 接受新的客户端连接
		{
			if (fds[i].revents & POLLIN)
			{
				struct sockaddr_in cli;
				socklen_t len = sizeof(cli);

				int c = accept(sockfd, (struct sockaddr*)&cli, &len);
				if (c < 0)
				{
					printf("accept error\n");
					continue;
				}

				printf("one client link success\n");
				InsertFd(fds, c, POLLIN | POLLRDHUP);
			}
		}
		else // 客户端连接文件描述符
		{
			// 必须先判断POLLRDHUP,若不为断开则接收数据,提高效率
			if (fds[i].revents & POLLRDHUP) // 客户端断开连接
			{
				printf("one client unlink\n");
				close(fds[i].fd);

				// 防止下次连接关闭的fd
				fds[i].fd = -1;
				fds[i].events = 0;
			}
			else if (fds[i].revents & POLLIN) // 有数据传递
			{
				char buff[128] = { 0 };
				int n = recv(fds[i].fd, buff, 127, 0);
				if (n <= 0)
				{
					printf("recv error\n");
					continue;
				}

				printf("%d: %s\n", fds[i].fd, buff);
				send(fds[i].fd, "OK", 2, 0);
			}
		}
	}
}

int main()
{
	int sockfd = InitSocket();
	assert(sockfd != -1);

	struct pollfd fds[FDS_LENGTH];
	InitFds(fds); // 初始化fds

	InsertFd(fds, sockfd, POLLIN); // 将连接套接字sockfd插入

	while (1)
	{
		int n = poll(fds, FDS_LENGTH, -1); // n>0返回就绪文件描述符的个数
		if (n <= 0)
		{
			printf("poll error\n");
			continue;
		}

		DealFinishEvent(fds, sockfd); 
	}

	close(sockfd);
	exit(0);
}

TCP客户端

代码参见TCP客户端

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值