基于TCP的多路复用

1. 知识点

目前支持I/O多路复用的系统调用有select,pselect,poll,epoll。与多进程和多线程技术相

比,I/O多路复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进

程/线程,从而大大减小了系统的开销。

I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般

是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都

是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是

阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用

户空间

2. select 函数

0 表示标准输入   STDIN_FILENO

1 表示标准输出     STDOUT_FILENO

2 表示标准错误输出 STDERR_FILENO

2.1 select存在三个问题

[1] 每次调用select,都需要把被监控的fds集合从用户态空间拷贝到内核态空间,高并发场景

下这样的拷贝会使得消耗的资源是很大的。

[2] 能监听端口的数量有限,单个进程所能打开的最大连接数由FD_SETSIZE宏定义,监听上

限就等于fds_bits位数组中所有元素的二进制位总数,32位机默认1024个,64位默2048。

[3] 被监控的fds集合中,只要有一个有数据可读,整个socket集合就会被遍历一次,用户线程并不知道哪些 fds 收到数据只能挨个遍历每个socket来收集可读事件了。

2.2 函数接口

1)用户进程需要监控某些资源 fds,在调用 select 函数后会阻塞,操作系统会将用户线程加入这些资源的等待队列中。

2)直到有描述符就绪(有数据可读、可写或有 except)或超时(timeout 指定等待时间,如果立即返回设为 null 即可),函数返回。

3)select 函数返回后,中断程序唤起用户线程。用户可以遍历 fds,通过 FD_ISSET 判断具体哪个 fd 收到数据,并做出相应处理。

select 函数优点明显,实现起来简单有效,且几乎所有操作系统都有对应的实现。

2.2.1 接口

int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval  *timeout);

2.2.2 参数:

int nfds:管理的最⼤的⽂件描述符+1

fd_set *readfds:⽂件描述符表(集合),监视管理⽂件描述符的读操作是否就绪

fd_set *writefds:⽂件描述符表(集合),监视管理⽂件描述符的写操作是否就绪,没有就 写NULL

fd_set *exceptfds:⽂件描述符表(集合),监视管理⽂件描述符的异常

struct timeval *timeout:timeout:超时设置。

Null:一直阻塞直到有文件描述符就绪或出错

时间值为0:仅仅检测文件描述符集的状态,然后立即返回

时间值不为0:在指定时间内,如果没有事件发生,则超时返回。

超时设置过后,如果select超时了,那么返回值是0, 并且超时时间的结构体会变成 0s

2.2.3 返回值:

成功:返回监视到就绪的⽂件描述符的个数,会把监视的表修改为只剩下就绪的⽂件描述符

失败:返回-1

2.2.4 操作⽂件描述符表:

void FD_CLR(int fd, fd_set *set);//把⽂件描述符fd从set表删除

int   FD_ISSET(int fd, fd_set *set);//判断fd是否在set集合中,返回值描述符存在集合里返回真1,不存在返回假0

void FD_SET(int fd, fd_set *set);//把fd 加⼊到set表,将 fd_set 结构中对应的位设置为1,表示便通过 select 函数对文件描述符 fd进行监视。

void FD_ZERO(fd_set *set);//清空表,将其所有位都设置为0。

2.3 多路复用实现通信

服务端select_serve.c

#include <stdio.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/time.h>
//基于TCP 的IO多路复用通信
//服务端
int main()
{

	//创建套接字
	int sock_fd = socket(PF_INET,SOCK_STREAM,0);
	
	//初始化本机地址和端口
	struct sockaddr_in srvaddr;
	srvaddr.sin_family = PF_INET;
	srvaddr.sin_port = htons(10000);
	srvaddr.sin_addr.s_addr = inet_addr("192.168.124.151");
	socklen_t srvaddr_len = sizeof(srvaddr);

	//绑定套接字
	bind(sock_fd,(struct sockaddr *)&srvaddr,srvaddr_len);
	//监听
	listen(sock_fd,4);
	
	//等待连接
	printf("等待连接中......\n");
	int conn_fd = accept(sock_fd,NULL,NULL);
	printf("连接成功!!\n");
	
	//定义一个集合
	fd_set jihe;

	while(1)
	{

		FD_ZERO(&jihe);//清空集合,将其所有位都设置为0。
	
		FD_SET(conn_fd,&jihe);//把conn_fd套接字(套接字说白了也是文件描述符)添加进集合

		//标准输入文件STDIN_FILENO-------0
		FD_SET(STDIN_FILENO,&jihe);//把标准输入添加进集合
		
		//多路复用的系统调用,这里监控读操作,
		//一旦有操作select就会被select监控到,接着配合后面的FD_ISSET()判断出具体时集合中的哪个文件描述符
		int ret = select(conn_fd+1,&jihe,NULL,NULL,NULL);
		if(-1 == ret)//监控失败
		{
			perror("select failed");
			continue;
		}
		if(0 == ret)//超时
		{
			continue;
		}
		
		//判断文件描述符是否在集合中 如果已连接套接字在集合,则进行读操作,读取客户端发来的信息
		if(FD_ISSET(conn_fd,&jihe) == 1)
		{
			char rbuf[128]={0};
			read(conn_fd,rbuf,sizeof(rbuf));
			printf("from cli:%s\n",rbuf);
			
			//FD_CLR(conn_fd,&jihe);
		}
		
		//如果我们有标准输入在集合中,则写入,即通过已连接套接字发送给客户端
		if(FD_ISSET(STDIN_FILENO,&jihe) == 1)//判断出是STDIN_FILENO标准输入
		{
			char wbuf[128]={0};//定义缓冲区
			fgets(wbuf,sizeof(wbuf),stdin);//键盘输入
			write(conn_fd,wbuf,sizeof(wbuf));
			
			//FD_CLR(STDIN_FILENO,&jihe);
		}
		
	}
	
}



客户端select_cilent.c

#include <stdio.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/time.h>
//基于TCP 的IO多路复用通信
//客户端
int main()
{
	//创建套接字
	int sock_fd = socket(PF_INET,SOCK_STREAM,0);
	
	//初始化服务端网络地址
	struct sockaddr_in srvaddr;
	srvaddr.sin_family = PF_INET;
	srvaddr.sin_port = htons(10000);
	srvaddr.sin_addr.s_addr = inet_addr("192.168.124.151");
	
	socklen_t srvaddr_len = sizeof(srvaddr);
	
	//这个绑定可有可无
	// bind(sock_fd,(struct sockaddr *)&srvaddr,srvaddr_len);
	
	//listen(sock_fd,4);
	
	//请求连接
	connect(sock_fd,(struct sockaddr *)&srvaddr,srvaddr_len);
	
	//定义一个集合
	fd_set jihe;

	
	while(1)
	{
		FD_ZERO(&jihe);//清空集合
	
		FD_SET(sock_fd,&jihe);//把sock_fd套接字(套接字说白了也是文件描述符)添加进集合
		//标准输入文件STDIN_FILENO-------0
		FD_SET(STDIN_FILENO,&jihe);//把标准输入添加进集合
		
		//多路复用的系统调用,这里监控读操作
		int ret = select(sock_fd+1,&jihe,NULL,NULL,NULL);
		if(-1 == ret)
		{
			perror("select failed");
			continue;
		}
		if(0 == ret)
		{
			continue;
		}
		
		//判断文件描述符是否在集合中 如果已连接套接字在集合,则进行读操作,读取客户端发来的信息
		if(FD_ISSET(sock_fd,&jihe) == 1)
		{
			char rbuf[128]={0};
			read(sock_fd,rbuf,sizeof(rbuf));
			printf("from srv:%s\n",rbuf);
			
			//FD_CLR(sock_fd,&jihe);
		}
		//如果我们有标准输入文件描述符在集合中,则写入,即通过已连接套接字发送给服务端
		if(FD_ISSET(STDIN_FILENO,&jihe) == 1)
		{
			char wbuf[128]={0};
			fgets(wbuf,sizeof(wbuf),stdin);
			write(sock_fd,wbuf,sizeof(wbuf));
			
			//FD_CLR(STDIN_FILENO,&jihe);
		}
		
	}
	
}



3. poll

3.1 优点

poll 函数与 select 原理相似,都需要来回拷贝全部监听的文件描述符,不同的是:

1)poll 函数采用链表的方式替代原来 select 中 fd_set 结构,因此可监听文件描述符数量不受限。

2)poll 函数返回后,可以通过 pollfd 结构中的内容进行处理就绪文件描述符,相比 select 效率要高。

3)新增水平触发:也就是通知程序 fd 就绪后,这次没有被处理,那么下次 poll 的时候会再次通知同个 fd 已经就绪。

3.2 缺点

和 select 函数一样,poll 返回后,需要轮询 pollfd 来获取就绪的描述符。poll和select同样

存在一个性能缺点就是包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,以及个别描述符就绪触发整体描述符集合的遍历的低效问题。而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。

4. epoll

epoll 使用一个文件描述符管理多个描述符,将用户进程监控的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间只需拷贝一次。

创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

4.1 优点

1)没有最大并发连接的限制,能打开的 FD 的上限远大于 1024。

2)效率提升,不是轮询的方式,不会随着 FD 数目的增加效率下降。

3)内存拷贝,利用 mmap() 文件映射内存加速与内核空间的消息传递,即 epoll 使用 mmap 减少复制开销。

4)新增 ET 模式。

5. 总结

5.1 select、poll、epoll区别

三种函数在的 Linux 内核里有都能够支持,其中 epoll 是 Linux 所特有,而 select 则应该是 POSIX 所规定,一般操作系统均有实现。

5.2 工作模式

1)LT模式

LT(level triggered)模式:也是默认模式,即当 epoll_wait 检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件,并且下次调用 epoll_wait 时,会再次响应应用程序并通知此事件。

2)ET模式

ET(edge-triggered)模式:当 epoll_wait 检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用epoll_wait时,不会再次响应应用程序并通知此事件。

ET 是一种高速工作方式,很大程度上减少了 epoll 事件被重复触发的次数。epoll 工作在 ET 模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/234577.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot中的事务是如何实现的?懂吗?

SpringBoot中的事务管理&#xff0c;用得好&#xff0c;能确保数据的一致性和完整性&#xff1b;用得不好&#xff0c;可能会给性能带来不小的影响哦。 基本使用 在SpringBoot中&#xff0c;事务的使用非常简洁。首先&#xff0c;得感谢Spring框架提供的Transactional注解&am…

Proteus仿真--串口发送数据到2片8×8点阵屏滚动显示

本文介绍2片88点阵屏滚动显示设计&#xff08;完整仿真源文件及代码见文末链接&#xff09; 仿真图如下 仿真运行视频 Proteus仿真--1602LCD显示电话拨号键盘按键实验&#xff08;仿真文件程序&#xff09; 附完整Proteus仿真资料代码资料 链接&#xff1a;https://pan.baidu…

Leetcode每日一题

https://leetcode.cn/problems/binary-tree-preorder-traversal/ 这道题目需要我们自行进行创建一个数组&#xff0c;题目也给出我们需要自己malloc一个数组来存放&#xff0c;这样能达到我们遍历的效果&#xff0c;我们来看看他的接口函数给的是什么。 可以看到的是这个接口函…

qt可以详细写的项目或技术

1.QT 图形视图框架 2.QT 模型视图结构 3.QT列表显示大量信息 4.QT播放器 5.QT 编解码 6.QT opencv

2023北京智慧城市与电气高峰论坛-安科瑞 蒋静

2023年7月27日&#xff0c;北京土木建筑学会电气设计委员会、北京电气设计技术协作及情报交流网联合举办的“北京电气设计第43届年会”在京盛大召开。安科瑞作为企业微电网能效管理平台服务商与广大同仁共聚本次盛会&#xff0c;尽享技术盛宴。 本次会议采用线上线下相结合&…

【C++】:红黑树

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家解读一下有关多态的知识点&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a;从入门到精通 数据结…

12.视图

目录 1.视图的含义与作用 2.视图的创建与查看 1.创建视图的语法形式 2、查看视图&#xff1a; 1.使用DESCRIBE语句查看视图基本信息 2.使用SHOW TABLE STATUS语查看视图基本信息查看视图的信息 3.使用SHOW CREATE VIEW语查看视图详细信息 4.在views表中查看视图详细信息…

【合集】SpringBoot——Spring,SpringBoot,SpringCloud相关的博客文章合集

前言 本篇博客是spring相关的博客文章合集&#xff0c;内容涵盖Spring&#xff0c;SpringBoot&#xff0c;SpringCloud相关的知识&#xff0c;包括了基础的内容&#xff0c;比如核心容器&#xff0c;springMVC&#xff0c;Data Access&#xff1b;也包括Spring进阶的相关知识&…

智能优化算法应用:基于正余弦算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于正余弦算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于正余弦算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.正余弦算法4.实验参数设定5.算法结果6.参考文…

【数电笔记】54-或非门构成的基本RS触发器

目录 说明&#xff1a; 1. 电路组成 2. 逻辑功能 3. 特性表 4. 特性方程 5. 例题 6. 两种基本RS触发器的形式比 说明&#xff1a; 笔记配套视频来源&#xff1a;B站&#xff1b;本系列笔记并未记录所有章节&#xff0c;只对个人认为重要章节做了笔记&#xff1b;标题前…

NAND闪存价格暴涨:512GB芯片翻倍,256GB涨幅达55%

此前&#xff0c;根据Trendforce的信息&#xff0c;今年第四季度NAND的合约价预计上涨8-13%&#xff0c;其中Wafer上涨13-18%。 根据DRAMeXchange最新的数据表明&#xff0c;之前预测的数据还是太保守了&#xff0c;过去一年Wafer NAND价格如下图&#xff1a; DRAM/NAND价格近几…

体系化学习运筹学基础算法的实践和总结

文章目录 引言目标设计目标实践文章汇总经验总结一则预告 引言 眨眼间已经12月了&#xff0c;眼看着2023年马上要过完了。 女朋友最近总说&#xff0c;工作以后感觉时间过的好快。事实上&#xff0c;我也是这么认为的。年纪越大&#xff0c;越会担心35岁危机的降临。所以&…

CESM笔记——component活动状态+compset前缀解析+B1850,BHIST区别

时隔一年没写CSDN笔记了&#xff0c;一些CESM的知识点我都快忘了。诶&#xff0c;主要是在国外办公室的网屏蔽了好多国内的网络&#xff0c;CSDN登不上&#xff0c;回家又不想干活。。。好吧&#xff0c;好多借口。。。 昨天师弟问我一些问题&#xff0c;想想要不可以水一篇小…

MySQL进阶学习--day01

存储引擎介绍 1. MySQL体系结构2. 存储引擎介绍2.1 存储引擎操作2.2 示例演示 1. MySQL体系结构 连接层&#xff08;Connection Layer&#xff09;&#xff1a;连接层主要负责与客户端建立连接&#xff0c;并进行用户身份验证和权限验证。在这一层&#xff0c;MySQL 接收来自客…

postgresql安装部署(docker版本)

1.在线部署 创建数据库存储目录 mkdir /home/pgdata创建容器 docker run --name postgresql --restartalways -d -p 5432:5432 -v /home/pgdata:/var/lib/postgresql/data --shm-size10g -e POSTGRES_PASSWORD密码 postgis/postgis:12-3.2-alpine–name为设置容器名称 -d表…

网页设计中增强现实的兴起

目录 了解增强现实 增强现实的历史背景 AR 和网页设计的交叉点 AR 在网页设计中的优势 增强参与度和互动性 个性化的用户体验 竞争优势和品牌差异化 AR 在网页设计中的用例 结论 近年来&#xff0c;增强现实已成为一股变革力量&#xff0c;重塑了我们与数字领域互动的方式。它被…

每天五分钟计算机视觉:使用1*1卷积层来改变输入层的通道数量

本文重点 在卷积神经网络中有很多重要的卷积核&#xff0c;比如1*1的卷积核&#xff0c;3*3的卷积核&#xff0c;本文将讲解1*1的卷积核的使用&#xff0c;它在卷积神经网络中具有重要的地位。由于1*1的卷积核使用了最小的窗口&#xff0c;那么1*1的卷积核就失去了卷积层可以识…

QEMU环境调试方法

目录 1.如何查看makefile构建过程执行的命令&#xff1f; 2.如何使用GCC生成C程序的宏展开文件&#xff1f; 3.如何在qemu中执行特定的可执行程序&#xff1f; 4.如何在qemu中直接运行可执行程序&#xff1f; 5.如何在qemu中调试某个可执行程序&#xff1f; 本文从调试的角…

Linux的权限

Linux的权限 一、shell运行原理--外壳程序二、Linux权限&#xff08;主体&#xff0c;重点&#xff09;三、常见的权限问题目录权限umask粘滞位 一、shell运行原理–外壳程序 为什么我们不是直接访问的操作系统&#xff1f; 1.人不善于直接使用操作系统 2.如果让人直接访问操作…

阵列信号处理-波束方向图参数

波束方向图的参数有&#xff1a; 3dB带宽(半功率波束宽度&#xff0c;HPBW&#xff0c;half-power beamwidth)到第一零点距离(这个距离的两倍称为 B W N N BW_{NN} BWNN​)到第一旁瓣的距离第一旁瓣的高度其余零点的位置旁瓣的衰减速率栅瓣 波束方向图的主波束 3dB波束宽度 3…