[C++网络协议] I/O复用
具有代表性的并发服务器端实现模型和方法:
多进程服务器:通过创建多个进程提供服务。
多路复用服务器:通过捆绑并统一管理I/O对象提供服务。✔
多线程服务器:通过生成与客户端等量的线程提供服务。
目录
1. I/O复用
2. select函数
2.1 select函数的作用
2.2 设置文件描述符
2.3 指定监视范围
2.4 设置超时
2.5 查看调用select函数后的结果
2.7 与Windows系统的区别
3. 实现I/O复用的回声服务器端
1. I/O复用
“在一个通信频道中传递多个数据(信号)的技术。”
“为了提高物理设备的效率,用最少的物理要素传递最多数据时使用的技术。”
举个例子,某个教师里有10名学生,1位老师,这10名学生都非等闲之辈,他们会不停的提问,所以学校没有办法,只能给他们每个人都配一个老师,这样这个教师就有10个老师,10个学生,但这样的话,以后每当有一个新学生进来,就要增加一个新老师,这样下去也不是办法。这时,学校来了个贼牛的老师,他一个人就可以应对所有学生的提问,而且速度很快,所以学校就把其他老师给转移到了其他班。并且,现在学生提问必须举手,老师确认学生的提问再回答问题。现在,这间教师就是以I/O复用方式运行的。

如图是I/O复用在服务端的模型。
2. select函数
#include<sys/select.h>
#include<sys/time.h>int select(
int maxfd, //监视对象文件描述符数量
fd_set* readset, //将所有关注"是否存在待读取数据"的文件描述符注册到fd_set型//变量,并传递其地址值
fd_set* writeset, //将所有关注"是否可传输无阻塞数据"的文件描述符注册到fd_set型//变量,并传递其地址值
fd_set* exceptset, //将所有关注"是否发生异常"的文件描述符注册到fd_set型变量,//并传递其地址值
const struct timeval* timeout //调用select函数后,为防止进入无限阻塞状态,传递超时信息
);
成功返回>0的值,表示发生上述事件的文件描述符
超时返回0
失败返回-1
struct timaval
{long tv_sec; //secondslong tv_usec; //microseonds
}
2.1 select函数的作用
作用:将多个文件描述符集中到一起统一监视。获取发生监视事件的文件描述符,从而与这个文件描述符指定的套接字进行通信。
监视事件:
1.是否存在套接字接收数据(read)
2.无需阻塞传输数据的套接字有哪些(write)
3.哪些套接字有异常(except)
2.2 设置文件描述符
select函数是怎么将多个文件描述符集中到一起监视的?
答:使用fd_set数组。
fd_set数组的结构:

fd_set数组是一个位数组,即只存储0与1。0:表示当前文件描述符未被监视,1:表示当前文件描述符正在被监视。
那么fd_set数组是怎么将文件描述符集中到数组里的?又是怎么设置它的位数的?
答:通过提供的宏来完成。
fd_set数组提供如下宏:
| 宏 | 含义 |
| FD_ZERO(fd_set* fdset); | 将fdset数组里的所有位初始化为0 |
| FD_SET(int fd,fd_set* fdset); | 将文件描述符fd的信息注册到fdset数组里,即指定一个位置为此文件描述符,并将其位设置为1。 |
| FD_CLR(int fd,fd_set* fdset); | 将fdset数组里的文件描述符fd的信息清除掉,即设置为0。 |
| FD_ISSET(int fd,fd_set* fdset); | 判断fdset数组里有没有注册文件描述符fd的信息,即指定位置处,其位的值是否为1。有则返回true,无则返回false。 |
所以使用select函数前的第一步,就是先把要监视的文件描述符注册到fd_set数组里。
2.3 指定监视范围
select函数通过第一个参数来传递监视对象的文件描述符的数量。因为Linux里文件描述符的值是从3开始递增的,所以你只需将最大的文件描述符的值再加1传递给select的第一个参数即可。
2.4 设置超时
struct timaval
{long tv_sec; //secondslong tv_usec; //microseonds
}
通过select函数的最后一个参数来设置超时,因为select函数只有在有文件描述符发生变化时,才会返回,否则会一直阻塞住。
所以如果你不想阻塞住程序,那么就可以设置超时时间,传递给select的最后一个参数。
如果你想阻塞住程序,直到有文件描述符发生变化,你可以给select的最后一个参数传nullptr。
当达到超时时间而没有文件描述符改变时,select函数返回0。
2.5 查看调用select函数后的结果
select函数在调用时,除发生变化的文件描述符对应位外,会把传递的fd_set数组里的其余位全部置为0。

如图,你传入select函数的fd_set数组里,要求监视的是文件描述符fd1、fd2、fd3等,然后,select函数就会将fd0、fd1、fd2、fd3等,没有发生变化的文件描述符置为0,发生变化的文件描述符就不改变。
所以,你调用select函数后,获取到的fd_set数组里的值,位为1的都是发生变化的文件描述符,然后你根据fd_set数组是第几个参数,从而可以对它进行指定的操作,例如:你传递的fd_set数组的参数是select函数的第二个参数,那么说明,这个fd_set数组里位为1的文件描述符,此时有输入流,你就可以通过read函数来将里面的数据取出了。
2.7 与Windows系统的区别
上述都是在Linux系统下的,select函数在Linux和Windows系统上,完全相同。只是:
1.在Windows系统上,select函数的第一个参数是无意义的,只是为了保持与Linux系统的兼容性。
2.Windows系统的fd_set与Linux的有区别。如下所示是Windows的fd_set结构体,其中数组也是位数组,并且使用的宏和Linux也是一样的。
typedef struct fd_set
{u_int fd_count; //套接字句柄数SOCKET fd_array[FD_SETSIZE]; //保存套接字句柄
}fd_set
为什么Windows要这样?
因为:在Linux中,文件描述符是递增的,所以你注册的时候,系统可以很好的找出当前文件描述符和最后生成的文件描述符之间的关系。但是在Windows中,套接字句柄(SOCKET)的生成并非是从0开始的,值之间也没有规律,所以需要直接保存句柄的数组和记录句柄数的变量。
3. 实现I/O复用的回声服务器端
实现思路:
创建一个fdset数组,里面要存放所有要监视的文件描述符。首先把server文件描述符注册到里面,接着select监听这个server文件描述符,因为TCP的连接,也是以数据接收的形式进行的,所以当有连接请求时,select函数里的readfdset数组里注册的server文件描述符就会置为1。紧接着就处理这个连接请求,把后面每连接的一个客户端的文件描述符就注册到fdset数组里,用循环,来遍历,只要是server文件描述符为1,就处理连接请求,不是,就是客户端文件描述符,就处理读写。
代码:
#include<iostream>
#include<sys/socket.h>
#include<unistd.h>
#include<signal.h>
#include<sys/wait.h>
#include<arpa/inet.h>
#include<cstring>
#include<sys/time.h>
#include<sys/select.h>int main()
{int socketfd=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);if(socketfd==-1){std::cout<<"socket fail!"<<std::endl;return 0;}int bReuse=true;socklen_t reuseLen=sizeof(bReuse);setsockopt(socketfd,SOL_SOCKET,SO_REUSEADDR,(void*)&bReuse,reuseLen);sockaddr_in serverAddr;memset(&serverAddr,0,sizeof(serverAddr));serverAddr.sin_family=AF_INET;serverAddr.sin_addr.s_addr=htonl(INADDR_ANY);serverAddr.sin_port=htons(9130);if(-1==bind(socketfd,(sockaddr*)&serverAddr,sizeof(serverAddr))){std::cout<<"bind fail!"<<std::endl;return 0;}if(-1==listen(socketfd,1)){std::cout<<"listen fail!"<<std::endl;return 0;}fd_set fdset;FD_ZERO(&fdset);FD_SET(socketfd,&fdset);timeval timeout;timeout.tv_sec=5;timeout.tv_usec=0;fd_set tempset;int fdmax=socketfd;while(1){tempset=fdset;int result=select(fdmax+1,&tempset,0,0,&timeout);if(result==0){continue;}else if(result==-1){std::cout<<"select fail"<<std::endl;break;}else{for(int i=0;i<fdmax+1;++i){if(FD_ISSET(i,&tempset)){if(i==socketfd) //服务器文件描述符发生了变化,意味着有连接请求{sockaddr_in clientAddr;memset(&clientAddr,0,sizeof(clientAddr));socklen_t clientLen=sizeof(clientAddr);int clientfd=accept(i,(sockaddr*)&clientAddr,&clientLen);std::cout<<"客户端IP地址:"<<inet_ntoa(clientAddr.sin_addr)<<std::endl;if(-1==clientfd){std::cout<<"accept fail!"<<std::endl;}else{FD_SET(clientfd,&fdset); //成功接收请求把客户端文件描述符信息注册到fdset里if(fdmax<clientfd)fdmax=clientfd;}}else //客户端文件描述符发生了变化,意味着有读取{char buff[1024];int readlen=read(i,buff,sizeof(buff));if(readlen==0) //客户端EOF,断开连接不再发送数据{FD_CLR(i,&fdset); //把fdset里的客户端文件描述符给注销掉close(i); //关闭客户端套接字}else{std::cout<<"客户端发来的消息:"<<buff<<std::endl;write(i,buff,readlen);}}}}}}close(socketfd);return 0;
}
Windows系统:
要注意,其fdset不是一个数组,而是一个结构体,要用“.”或“->”运算符来取里面的套接字文件描述符数组,来进行处理。
相关文章:
[C++网络协议] I/O复用
具有代表性的并发服务器端实现模型和方法: 多进程服务器:通过创建多个进程提供服务。 多路复用服务器:通过捆绑并统一管理I/O对象提供服务。✔ 多线程服务器:通过生成与客户端等量的线程提供服务。 目录 1. I/O复用 2. select函…...
3D数据导出工具HOOPS Publish:3D数据查看、生成标准PDF或HTML文档!
HOOPS中文网http://techsoft3d.evget.com/ 一、3D导出SDK HOOPS Publish是一款功能强大的SDK,可以创作丰富的工程数据并将模型文件导出为各种行业标准格式,包括PDF、STEP、JT和3MF。HOOPS Publish核心的3D数据模型是经过ISO认证的PRC格式(ISO 14739-1:…...
[羊城杯 2023] web
文章目录 D0nt pl4y g4m3!!! D0n’t pl4y g4m3!!! 打开题目,可以判断这里为php Development Server 启动的服务 查询得知,存在 PHP<7.4.21 Development Server源码泄露漏洞(参考文章) 抓包,构造payload 得到源码 class Pro{private $ex…...
Redisson—独立节点模式和集群管理工具
一、集群管理工具 Redisson集群管理工具提供了通过程序化的方式,像redis-trib.rb脚本一样方便地管理Redis集群的工具。 1、 创建集群 以下范例展示了如何创建三主三从的Redis集群。 ClusterNodes clusterNodes ClusterNodes.create() .master("127.0.0.1:…...
基于RabbitMQ的模拟消息队列之五——虚拟主机设计
文章目录 一、创建VirtualHost类二、初始化三、API1.创建交换机2.删除交换机3.创建队列4.删除队列5.创建绑定6.删除绑定7.发送消息转发规则 8.订阅消息1.消费者管理2.推送消息给消费者 3.添加一个消费者管理ConsumerManager9.确认消息 创建VirtualHost类。 1.串起内存和硬盘的数…...
Hadoop的概述与安装
Hadoop的概述与安装 一、Hadoop内部的三个核心组件1、HDFS:分布式文件存储系统2、YARN:分布式资源调度系统3、MapReduce:分布式离线计算框架4、Hadoop Common(了解即可) 二、Hadoop技术诞生的一个生态圈数据采集存储数…...
进程、线程与构造方法
进程、线程与构造方法 目录 一. 进程与线程1. 通俗解释2. 代码实现3. 线程生命周期(图解) 二. 构造方法 一. 进程与线程 1. 通俗解释 进程:就像电脑上运行的软件,例如QQ等。 线程:…...
04 Linux补充|C/C++
目录 Linux补充 C语⾔ C语言中puts和printf的区别? Linux补充 (1)ubuntu安装ssh服务端openssh-server命令: ubuntu安装后默认只有ssh客户端,只能去连其它ssh服务器;其它客户端想要连接这个ubuntu系统,需要安装部署…...
利用python制作AI图片优化工具
将模糊图片4K高清化效果如下: 优化前的图片 优化后如下图: 优化后图片变大变清晰了效果很明显 软件界面如下: 所用工具和代码: 1、所需软件包 网盘链接:https://pan.baidu.com/s/1CMvn4Y7edDTR4COfu4FviA提取码&…...
React v6(仅支持函数组件,不支持类组件)与v5版本路由使用详情和区别(详细版)
1.路由安装(默认安装最新版本6.15.0) npm i react-router-dom 2.路由模式 有常用两种路由模式可选:HashRouter 和 BrowserRouter。 ①HashRouter:URL中采用的是hash(#)部分去创建路由。 ②BrowserRouter:URL采用真实的URL资源,…...
(数字图像处理MATLAB+Python)第十二章图像编码-第一、二节:图像编码基本理论和无损编码
文章目录 一:图像编码基本理论(1)图像压缩的必要性(2)图像压缩的可能性A:编码冗余B:像素间冗余C:心理视觉冗余 (3)图像压缩方法分类A:基于编码前后…...
【Unity编辑器扩展】| 顶部菜单栏扩展 MenuItem
前言【Unity编辑器扩展】 | 顶部菜单栏扩展 MenuItem一、创建多级菜单二、创建可使用快捷键的菜单项三、调节菜单显示顺序和可选择性四、创建可被勾选的菜单项五、右键菜单扩展5.1 Hierarchy 右键菜单5.2 Project 右键菜单5.3 Inspector 组件右键菜单六、AddComponentMenu 特性…...
golang读取键盘功能按键输入
golang读取键盘功能按键输入 需求 最近业务上需要做一个终端工具,能够直接连到docker容器中进行交互。 技术选型 docker官方提供了python sdk、go sdk和remote api。 https://docs.docker.com/engine/api/sdk/ 因为我们需要提供命令行工具,因此采用g…...
用sklearn实现线性回归和岭回归
此文为ai创作,今天写文章的时候发现创作助手限时免费,想测试一下,于是就有了这篇文章,看的出来,效果还可以,一行没改。 线性回归 在sklearn中,可以使用线性回归模型做多变量回归。下面是一个示…...
结构型模式-桥接模式
用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。 这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类…...
缓存的放置时间和删除时间
缓存的放置时间和删除时间是指缓存中存储的数据的生命周期。这两个时间点非常重要,因为它们决定了缓存数据的有效期和何时应该从缓存中删除。 缓存的放置时间(Cache Put Time):这是指数据首次放入缓存的时间点。当数据被放入缓存时…...
内网穿透实战应用-如何通过内网穿透实现远程发送个人本地搭建的hMailServer的邮件服务
文章目录 1. 安装hMailServer2. 设置hMailServer3. 客户端安装添加账号4. 测试发送邮件5. 安装cpolar6. 创建公网地址7. 测试远程发送邮件8. 固定连接公网地址9. 测试固定远程地址发送邮件 hMailServer 是一个邮件服务器,通过它我们可以搭建自己的邮件服务,通过cpolar内网映射工…...
ensp基础命令大全(华为设备命令)
路漫漫其修远兮,吾将上下而求索 今天写一些曾经学习过的网络笔记,希望对您的学习有所帮助。 OSPF,BGP,IS-IS的命令笔记没有写上来,计划单独写,敬请期待,或者您可以在这个网站查查 : 万能查询网站 …...
thinkphp6 入门(4)--数据库操作 增删改查
一、设计数据库表 比如我新建了一个数据库表,名为test 二、配置数据库连接信息 本地测试 直接在.env中修改,不用去config/database.php中修改 正式环境 三、增删改查 引入Db库 use think\facade\Db; 假设新增的控制器路径为 app\test\control…...
MyBatisPlus 基础实现(一)
说明 创建一个最基本的MyBatisPlus项目,参考官网。 依赖 MyBatisPlus 依赖,最新版是:3.5.3.2 (截止2023-9-4)。 <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-bo…...
wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...
uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...
tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...
【UE5 C++】通过文件对话框获取选择文件的路径
目录 效果 步骤 源码 效果 步骤 1. 在“xxx.Build.cs”中添加需要使用的模块 ,这里主要使用“DesktopPlatform”模块 2. 添加后闭UE编辑器,右键点击 .uproject 文件,选择 "Generate Visual Studio project files",重…...
