【计算机网络】——I/O复用之poll

2023-11-02

1、poll的概述

在上一篇文章中,我们详细的介绍了I/O复用技术中的select使用。这篇文章我们来主要介绍一下poll.
poll系统调用和select类似,也是在指定事件内轮询一定数量的文件描述符,以测试其中是否有就绪的
本质都是统一监听,如果任意一个文件描述符上有关注的事件发生。则这个文件描述符以及发生的事件通知给应用程序。

2、poll函数的功能和作用

1、函数原型

int poll(struct pollfd fds[],int len,int timeout);

参数1:struct pollfd fds[]参数
1.1结构体描述
fds参数是一个pollfd结构类型的数组,它指定所有我们感兴趣的文件描述符上发生的可读、可写和异常等事件,pollfd结构体的定义如下:

Struct pollfd
{
	int fd;//用户设置关注的文件描述符(用户填充),不是二进制了,是int类型
	Short events;//用户关注的事件类型(用户填充)
	Short revents;//由内核填充,就绪的事件类型
}

我们每次调用poll之前要定义这个结构体,并且用户事件描述符对fd,events进行初始化。返回时就绪事件就在revents里面存储。具体的对结构体的操作如下图所示:
在这里插入图片描述
从这个结构中我们可以看到用户初始描述符和内核返回的就绪描述符是分开的,所以不用每次初始化它,初始化一次就可。但我们仍然不知道哪几个描述符就绪,只知道有几个就绪。

1.2用户关注的事件类型
我们知道Select通过三个参数传递只能关注三种事件类型。而poll能够关注的时间类型就较多。
每个结构体的 events 域是由用户来设置,告诉内核我们关注的是什么,而 revents 域是返回时内核设置的,以说明对该描述符发生了什么事件。

在这里插入图片描述
其中,POLLIN、POLLOUT和POLLRDHUP是常用的。POLLRDHUP是专门捕捉客户端断开异常的,我们在select时通过recv<=0来判断客户端是否关闭,在这专门提供了函数。

参数2:int len参数
主要指参数一pollfd结构类型的数组的长度。poll没有空间限制,可以存放很多很多个,不像select只能存储1024个。

参数3:timeout参数
定时时间,在一定时间内没有检测到就绪事件,返回0,为NULL表示永久阻塞

3、poll的特点

优点:

  1. 在pollfd结构体中我们可以明确的看到。poll的文件描述符是通过整型值去记录的。所以其能处理的文件描述符的个数就比select要大得多。而且是由用户自己指定的而select中的fd_set就只有系统内核提供的1024位。所以在应付大数目的文件描述符的时候相比于select速度更快。
  2. 因为poll将用户关注的和内核填充的分开可以,用户关注的事件类型是不会变的,所以初始化一次就可以。
  3. 相比于select关注的事件类型更多

缺点:

  1. 大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义,效率低下
  2. 与select一样,poll返回后,需要轮询pollfd来获取就绪的描述符

4、代码实现I/O复用poll

代码的实现和select类似,但是主要有以下值得注意的点。

  • select每次要初始化结构体,因为用户和内核共用一个空间。poll不用,一次初始化,每次只用对结构体中的fd,events成员进行一次初始化,记录用户事件描述符,有新连接,添加到结构体中,连接断开,从结构体中删除。内核返回的就绪事件在revents中放着
  • select在处理事件时比较简单,只需要判断是连接(添加进去)还是数据连接(处理数据)。而poll比较复杂,它需要判断监听,数据。数据连接时需要判断三种情况:没有事件发生;客户端自动断开连接事件发生;可读事件发生

那我们现在实现客户端连接服务器,发送数据,服务器收到给客户端回复ok。程序流程图参见博客poll详解

#define _GNU_SOURCE//添加事件类型的时候需要用到这个宏
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>

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

#define SIZE 100

int CreateSocket()
{
	int listenfd = socket(AF_INET, SOCK_STREAM, 0);
	if(listenfd == -1) return -1;

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

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

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

	return listenfd;
}

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

void AddFd(struct pollfd *fds,  int fd)
{
	int i = 0;
	for(; i < SIZE; ++i)
	{
		if(fds[i].fd == -1)
		{
			fds[i].fd = fd;
			fds[i].events = POLLIN | POLLRDHUP;//事件类型设置上去 但是针对于listenfd是不要POLLRDHUP这个宏。所以在主函数中要进行处理
			break;
		}
	}
}
void DelFd(struct pollfd  *fds, int fd)
{
	int i = 0;
	for(; i < SIZE; ++i)
	{
		if(fds[i].fd == fd)
		{
			fds[i].fd = -1;
			break;
		}
	}
}

void GetNewLink(struct pollfd *fds, int listenfd)
{
	struct sockaddr_in cli_addr;
	socklen_t len = sizeof(cli_addr);
	int c = accept(listenfd, (struct sockaddr*)&cli_addr, &len);
	if(c == -1)  return;

	printf("%d link success\n", c);
	AddFd(fds, c);
}

void DealOneClientData(struct pollfd *fds, int fd)
{
	char buff[128] = {0};
	int n = recv(fd, buff, 127, 0);
	if(n <= 0)
	{
		close(fd);
		DelFd(fds, fd);
		return;
	}

	printf("%d: %s\n", fd, buff);

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

void DealReadyEvent(struct pollfd *fds, int listenfd)
{
	int i = 0;
	for(; i < SIZE; ++i)
	{
		if(fds[i].fd == -1) continue;//不是有效的文件描述符
		
		//看这个文件描述符所关注的事件有没有发生。一般POLLRDHUP事件发生则POLLIN事件也一定发生
		//策略:先判断文件描述符再去判断事件类型
		if(fds[i].fd == listenfd && fds[i].revents & POLLIN)
		{
			GetNewLink(fds, listenfd);
		}
		else if(fds[i].revents & POLLRDHUP)//这种事件类型发生代表是普通的第二类文件描述符
		{
			close(fds[i].fd);
			fds[i].fd = -1;
			printf("one client unlink\n");
		}
		else if(fds[i].revents & POLLIN)//有数据到达
		{
			DealOneClientData(fds, fds[i].fd);
		}
	}
}

int main()
{
	int listenfd = CreateSocket();
	assert(listenfd != -1);

	struct pollfd fds[SIZE];
	InitFds(fds);//初始化的时候只有一个listendfd在里面
//针对listenfd文件类型要进行单独设置
	fds[0].fd = listenfd;
	fds[0].events = POLLIN;

	while(1)
	{
		int n = poll(fds, SIZE, -1);
		assert(n != -1);
		if(n == 0)//超时的
		{
			printf("time out\n");
			continue;//继续监听
		}

		DealReadyEvent(fds, listenfd);//处理就绪事件
	}

	close(listenfd);
	exit(0);
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【计算机网络】——I/O复用之poll 的相关文章

  • STM32系列 (一)开发环境的搭建

    STM32简介 STM32是意法半导体 ST 推出一款32位的单片机 STM32具有超低的价格 超多的外设 丰富的型号 优异的实时性 极低的开发成本等优势 STM32凭借其产品线的多样化 极高的性价比 简单易用的库开发方式 迅速在众多32位

随机推荐

  • 一、SQL Server列名显示无效却可以运行问题解决?

    一 SQL Server列名显示无效却可以运行问题解决 在SQLServer中 当设计 修改 表结构之后 再用SQL语句时 会出现列名无效 然后却可以运行 如下图 出现这种情况的原因是SQL Server的intellisense 智能感知
  • C语言volatile关键字的作用

    volatile是易变的 不稳定的意思 volatile关键字和const一样是一种类型修饰符 用它修饰的变量表示可以被某些编译器未知的因素改变 比如操作系统 硬件或者其它线程等 遇到这个关键字声明的变量 编译器对访问该变量的代码就不再进行
  • TypeError: Cannot read properties of undefined (reading ‘licenseNum‘) at Proxy

    这是因为在定义的时候 我们只定义了一层的结构 比如 info 其实后端返回的是 info goods goodsName cheng 此时调用goodsName info goods goodsName 就会报错info goods und
  • 2021-12-24 vue项目兼容IE

    Vue 不支持 IE8 及以下版本 因为 Vue 使用了 IE8 无法模拟的 ECMAScript 5 特性 但对于 IE9 Vue 底层是支持 vue cli4脚手架搭建的前端项目 vue版本2 6 12 browserslist配置 指
  • System.Single

    浮点 类型 别名 float System Single double System Double decimal System Decimal 字符 类型 别名 允许的值 bool System Boolean true flase ch
  • 跟阿铭学Linux第六章答案,Linux磁盘管理

    hda一般是指IDE接口的硬盘 hda指第一块硬盘 hdb指第二块硬盘 等等 sda一般是指SATA接口的硬盘 sda指第一块硬盘 sdb指第二块硬盘 等等 du b显示的是文件的实际大小 du k显示的是文件占用的磁盘块的大小 所以磁盘块
  • 性能测试压力测试

    性能测试指标 并发用户数 TPS Transaction Per Second 每秒事务数 系统的性能由TPS决定 mysql 记一次接口压力测试与性能调优 Apache JMeter是Apache组织开发的基于Java的压力测试工具 用于
  • 最长公共子序列 蓝桥杯 1189

    题目描述 给定一个长度为n数组A和一个长度为m数组B 请你求出它们的最长公共子序列长度为多少 输入描述 输入第一行包含两个整数n m 第二行包含n个整数ai 第三行包含m个整数bi 1 lt n m lt 10 3 1 lt ai bi l
  • BearPi-HM Nano HarmonyOS 环境搭建

    BearPi HM Nano HarmonyOS 环境搭建 BearPi HM Nano 介绍 BearPi HM Nano开发板是一块专门为鸿蒙OS设计的鸿蒙OS开发板 板载高度集成的2 4GHz WLAN SoC芯片Hi3861 并板载
  • java mongodb 关闭连接_未释放资源的教训,开发MongoDB连接一定要关闭连接

    废不少工夫将数据存储 全部迁移至mongodb 未作大量改动则是主因 但遇到奇怪的现象 程序跑起不久后 mongodb即假死 另起客户端想登陆mongodb都不成 要重启mongodb服务器才好转 分析原因是mongodb的连接池满掉且不释
  • elasticsearch学习之cross_fields字段使用

    cross fields字段 在查询阶段解决信号冲突问题 问题 The cross fields type is particularly useful with structured documents where multiple fi
  • Java进阶--Java垃圾回收机制全面解析

    本文同步发布在我的个人博客 如需转载请注明出处 提起Java的垃圾回收机制大家应该都有所了解 它不仅是面试的常客 也是Java体系中相当重要的一块知识 深入理解Java的GC机制 不仅有助于我们在开发中提高程序的性能 更有了在面试官面前炫
  • MySQL索引怎么用?究竟能有多快?看完这篇你就懂了!

    本文介绍的是MySQL索引的具体使用 原理以及本质结构请看我写的上一篇文章 MySQL索引凭什么能让查询效率提高这么多 原理是什么 目录 索引使用的优势 索引使用的劣势 1 维护成本高 2 所占空间大 索引的分类 索引的创建规则 索引的CR
  • 约瑟夫环(数组解决)

    约瑟夫环问题是一个很经典的问题 一个圈共有N个人 N为不确定的数字 第一个人的编号为0或者1 两个都可以 看你的程序如何编写 假设这边我将第一个人的编号设置为1号 那么第二个人的编号就为2号 第三个人的编号就为3号 第N个人的编号就为N号
  • photoshop不能保存bmp格式

    文章目录 前言 一 显示图层无法导出当前的bmp格式图片 二 图层合并 总结 前言 使用位图进行深度学习的特征点标定时间比正常图片格式的时间要缩短一半 所以使用ps新增异常点 但是新增图层后无法保存bmp的格式 一 显示图层无法导出当前的b
  • 详解Spring Ioc(控制反转)

    Spring Ioc 控制反转是一个比较抽象的概念 可以举例来说明 生活中 人们要用到一种东西 人们的基本想法就是找到东西 比如想喝果汁 在没有饮品店的日子里 最直接的做法就是 买果汁机 水果 准备开水 这时我们 主动 创造的过程 也就是一
  • Python自动化测试——基础理论思维导图

    1 自动化测试的定义 2 自动化测试的优势 3 自动化测试和手工测试相比 有哪些区别 4 主流的测试用具 5 自动化测试的流程 6 适用于自动化测试的条件 7 选择自动化测试技术时以语言为主的技术线 主要用到的自动化测试工具是Seleniu
  • ​兔子快跑/rabbit-UI和接口自动化测试平台​

    Rabbit 是一个开源的自动化测试平台 基于经典技术组合 Spring Boot Spring Security MyBatis Jwt Vue 目前版本已支持UI自动化和接口自动化 平台采用关键字驱动 测试人员无需任何代码基础 即可轻松
  • ubuntu18.04解决因没有集成显卡驱动进不去界面问题

    1 问题及设备描述 设备 工控机 ubuntu18 04 i9cpu 集成显卡 问题 更化软件源时不小心将微软Microsoft等的源给删除了 没有备份 查资料发现集显就在主板上 与cpu有很大关系 于是觉得问题在于将集成显卡驱动软件的源给
  • 【计算机网络】——I/O复用之poll

    文章目录 1 poll的概述 2 poll函数的功能和作用 3 poll的特点 4 代码实现I O复用poll 1 poll的概述 在上一篇文章中 我们详细的介绍了I O复用技术中的select使用 这篇文章我们来主要介绍一下poll po