221-I/O复用函数的使用(poll)

I/O复用函数的使用(poll)

poll是传入一个数组,数组大小可以超过1024。比select可以支持更多的描述符。
poll的事件类型更丰富。

poll 的接口介绍
poll 系统调用和 select 类似,也是在指定时间内轮询一定数量的文件描述符,以测试其中是否有就绪者。poll 的原型如下:

#include <poll.h>

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


poll 系统调用成功返回就绪文件描述符的总数,超时返回 0,失败返回-1
nfds 参数指定被监听事件集合 fds 的大小。
timeout 参数指定 poll 的超时值,单位是毫秒,timeout 为-1 时,poll 调用将永久
阻塞,直到某个事件发生,timeout 为 0 时,poll 调用将立即返回。

fds 参数是一个 struct pollfd 结构类型的数组,它指定所有用户感兴趣的文件描述
符上发生的可读、可写和异常等事件。pollfd 结构体定义如下:
struct pollfd
{
int fd; // 文件描述符
short events; // 注册的关注事件类型
short revents; // 实际发生的事件类型,由内核填充
};

其中,fd 成员指定文件描述符,events 成员告诉 poll 监听 fd 上的哪些事件类型。假如有16个位,则可以表示16种不同的事件。某个位为1代表相应的事情。
它是一系列事件的按位或,revents 成员则有内核修改,通知应用程序 fd 上实际发生了哪些事件。

返回值n>0,是这个数组上有n个描述符有数据,但是不知道是哪n个。
poll 支持的事件类型
在这里插入图片描述
每个位为1代表相应的事情。
在这里插入图片描述
赋值成2种事件的写法如下:
在这里插入图片描述
在这里插入图片描述
内核会通知我们用户实际发生了哪些事件。
在这里插入图片描述

revents是内核检测后返回给我们的。进行按位与判断!!!
在这里插入图片描述
我们先传入数组,数组的每个元素就是我们想要关注的描述符及其事件。
然后传入数组元素的个数,最后传入timeout,
如果我们定义的数组大小是100个,但是我们可能只传了10个描述符,那么我们怎么知道几个是描述符是有数据的?我们把结构体中不用的元素的fd置为-1,在内核中,发现描述符是-1,是无效描述符,就不会去检测什么事件了。只会去检测数组中真实有效的描述符。数组操作起来方便!

poll执行可能会阻塞,直到我们检测到数组描述符有事件产生了或者超时了。

poll 的示例代码

使用 poll 实现的 TCP 服务器代码如下:
在这里插入图片描述
封装函数如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
主函数如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
编译运行程序
运行服务器端和客户端
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
现在服务器端数组中有几个描述符?
答案是2个,一个是sockfd,一个是c。

在这里插入图片描述
现在服务器端数组有3个描述符。

我们把服务器端的代码修改一下,一次只读取一个字符!

在这里插入图片描述这样,我们发送一个hello,它一次recv只能回复一个字符h,回复e,要等到poll第二次返回,再次提醒我们这个描述符有数据,然后recv。
多次recv,poll返回多次。
在这里插入图片描述
为什么只看到2个ok,就是我们上次说的,还有一些ok在接收缓冲区放着!
在这里插入图片描述

还有4对ok在接收缓冲区。

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

#define DATALEN 1024
#define MAX_FD 128

// 初始化服务器端的 sockfd 套接字
int InitSocket()
{
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd == -1) return -1;

	struct sockaddr_in saddr;

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

	int res = bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr));
	if(res == -1) return -1;

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

	return sockfd;
}

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

void InsertFdToPollfds(struct pollfd fds[], int fd, short events)
{
	int i = 0;
	for (; i < MAX_FD; ++i)
	{
		if (fds[i].fd == -1)
		{
			fds[i].fd = fd;
			fds[i].events = events;
			break;
		}
	}
}

void GetClinetLink(struct pollfd fds[], int sockfd)
{
	struct sockaddr_in caddr;
	memset(&caddr, 0, sizeof(caddr));
	socklen_t len = sizeof(caddr);
	int c = accept(sockfd, (struct sockaddr*)&caddr, &len);
	if (c < 0)
	{
		return;
	}

	printf("A client connection was successful\n");

	InsertFdToPollfds(fds, c, POLLIN | POLLRDHUP);
}

// 处理客户端数据
int DealClientData(int clifd)
{
	char data[DATALEN] = { 0 };
	int n = recv(clifd, data, DATALEN - 1, 0);
	if(n <= 0)
	{
		return -1;
	}

	printf("%d: %s\n", clifd, data);
	send(clifd, "OK", 2, 0);
	return 0;
}

// 处理 poll 返回的就绪事件
void DealReadyEvent(struct pollfd fds[], int sockfd)
{
	int i = 0;
	for (; i < MAX_FD; ++i)
	{
		if (fds[i].fd == -1)
		{
			continue;
		}
		/* POLLRDHUP 用于判断客户端程序是否关闭
		else if (fds[i].revents & POLLRDHUP)
		{
		close(fds[i].fd);
		fds[i].fd = -1;
		printf("A client disconnected\n");
		continue;
		}*/
		else if (fds[i].revents & POLLIN)
		{
			if (fds[i].fd == sockfd)
			{
				GetClinetLink(fds, sockfd);
			}
			else
			{
				if(-1 == DealClientData(fds[i].fd))
				{
					close(fds[i].fd);
					fds[i].fd = -1;
					printf("A client disconnected\n");
				}
			}
		}
		else
		{
			printf("error\n");
		}
	}
}

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

	struct pollfd fds[MAX_FD];
	InitPollFds(fds);
	InsertFdToPollfds(fds, sockfd, POLLIN);

	while (1)
	{
		int n = poll(fds, MAX_FD, 2000);
		if (n < 0)
		{
			printf("poll error\n");
			continue;
		}
		else if (n == 0)
		{
			printf("timeout\n");
			continue;
		}
		else
		{
			DealReadyEvent(fds, sockfd);
		}
	}

	exit(0);
}

返回以后我们遍历的是所有描述符,并不是前n个都是有效的,无效的会置为-1.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林林林ZEYU

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值