[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…...
构建高性能WebSocket聊天应用:libwebsockets实战指南
构建高性能WebSocket聊天应用:libwebsockets实战指南 【免费下载链接】libwebsockets canonical libwebsockets.org networking library 项目地址: https://gitcode.com/gh_mirrors/li/libwebsockets Libwebsockets是一个简单易用、MIT许可证、纯C语言编写的…...
保姆级教程:用Python和Paho-MQTT库5分钟搭建你的第一个物联网通信Demo
5分钟实战:用PythonPaho-MQTT构建物联网通信原型 在智能家居设备突然向你手机推送报警消息时,在共享单车锁车后立即完成计费时,背后都是MQTT协议在高效运作。作为物联网领域的"HTTP协议",MQTT凭借其轻量级和发布/订阅模…...
消息队列5-RabbitMQ的高级特性和MQ的应用问题与解决方案-事务、消息分发的应用、幂等性保证、顺序性保证、消息积压的解决
文章目录一. 事务1. 模版开启事务功能2. 配置事务管理器3. 声明队列4. 生产者5. 运行图二. 消息分发1. 限流(1) yml配置文件(2) 声明队列与交换机及绑定关系(3) 生产者(4) 消费者(5) 运行图2. 负载均衡(1) yml配置(2) 消费者代码(3) 运行图三. MQ的幂等性保证1. MQ中存在的问题…...
用Notepad++打开PLY文件:手把手教你读懂三维点云与网格数据的‘源代码’
用Notepad打开PLY文件:手把手教你读懂三维点云与网格数据的‘源代码’ 当你第一次拿到一个PLY文件时,可能会感到困惑——这个看似普通的文本文件,如何承载复杂的三维世界?就像程序员通过阅读源代码理解软件逻辑一样,我…...
electron+ruoyi-vue深度整合指南:从web到桌面的完整改造方案
ElectronRuoYi-Vue企业级桌面应用深度整合实战 企业级应用从Web向桌面端迁移已成为提升用户体验的重要路径。作为国内广泛使用的开源后台管理系统,RuoYi-Vue与Electron的结合能够快速构建跨平台桌面应用。但真正实现企业级稳定运行,需要解决主进程通信、…...
基于stm32的通信系统,sim800c与服务器通信,无线通信监测,远程定位,服务器通信系统...
基于stm32的通信系统,sim800c与服务器通信,无线通信监测,远程定位,服务器通信系统,gps,sim800c,心率,温度,stm32 由STM32F103ZET6单片机核心板电路、DS18B20温度传感器电…...
如何突破音频收听限制?打造个人离线音频库的完整方案
如何突破音频收听限制?打造个人离线音频库的完整方案 【免费下载链接】xmly-downloader-qt5 喜马拉雅FM专辑下载器. 支持VIP与付费专辑. 使用GoQt5编写(Not Qt Binding). 项目地址: https://gitcode.com/gh_mirrors/xm/xmly-downloader-qt5 作为音频爱好者&a…...
Windows Defender彻底移除指南:如何安全禁用系统安全组件并提升30%性能
Windows Defender彻底移除指南:如何安全禁用系统安全组件并提升30%性能 【免费下载链接】windows-defender-remover A tool which is uses to remove Windows Defender in Windows 8.x, Windows 10 (every version) and Windows 11. 项目地址: https://gitcode.co…...
游戏开发实战:Unity中合并带材质的.obj模型文件全攻略
Unity游戏开发实战:高效合并带材质的.obj模型文件全流程解析 在游戏开发中,资源优化始终是提升性能的关键环节。当项目涉及大量.obj格式的3D模型时,合并这些文件不仅能减少Draw Call,还能显著简化资源管理流程。本文将深入探讨如何…...
终极指南:如何快速为设计添加地图填充效果 - Sketch Map Generator 插件完全解析
终极指南:如何快速为设计添加地图填充效果 - Sketch Map Generator 插件完全解析 【免费下载链接】sketch-map-generator Sketch plugin to fill a shape with a map generated from a given location using Google Maps and Mapbox 项目地址: https://gitcode.co…...
