多路转接之select(fd_set介绍,参数详细介绍),实现非阻塞式网络通信

news2024/9/17 7:16:33

目录

多路转接之select

引入

介绍

fd_set

函数原型

nfds

readfds / writefds / exceptfds

readfds 

总结 

fd_set操作接口 

timeout

timevalue 结构体

传入值

返回值

代码

注意点 -- 调用函数

select的参数填充 

获取新连接

注意点 -- 通信时的调用函数

添加新fd到位图中

处理函数


多路转接之select

引入

io本质+io效率本质,5种io模型(介绍,异步/同步区别,阻塞/非阻塞区别)-CSDN博客

以前使用的io接口,既完成等待,又完成拷贝

但在多路转接的io方式中不同,分为两个部分,需要调用两个函数来完成

介绍

select只负责等待,一次可以等待多个fd

  • 就像之前钓鱼例子中的d,他拥有多个鱼竿,就相当于等待多个fd

既然可以关注多个fd,自然参数中就要使用其他数据结构了 -- fd_set

fd_set

内核提供的一种数据类型

  • 位图

因为fd_set是一个具体的类型

  • 既然是类型,就一定有大小
  • 有大小就会有比特位的数量
  • 也就相当于可以等待的文件fd值文件数量是有上限的

使用sizeof测试fd_set的大小,得到它是1024个bit

  • 所以一次最多等待1024个文件的某个事件
  • 这个值随着系统不同会有变化,实际应该动态计算 -- sizeof(fd_set) * 8

函数原型

nfds

要等待的多个fd中的最大值+1

readfds / writefds / exceptfds

等待多个fd的关键,属于输入输出型参数

等待 -- 等待事件就绪

  • 事件 -- 一般分为 读/写/有异常
  • 只要读写事件就绪,就可以直接完成拷贝操作,不会阻塞住
  • 异常事件是例外,需要特殊处理,这里不做介绍

如果想关注某个文件上的读事件,就把该文件的fd设置进readfds

  • 其他同理
  • 可以关注同一个文件上的多个事件,也可以分顺序地关注,总之设置进相应位图中就行

接下来我们以readfds为例,详细介绍一下,其他位图同理 

readfds 

fd本身就是从0开始的数字

  • 和数组下标/位图均可以一一对应

因为是输入输出型参数:

输入时

  • 我们要告诉内核需要关注的fd集,你要帮我关心这些文件上面的读事件 + 这是个位图结构 + fd和位图可以对应
  • 所以,可以得出,位图上的比特位位置(从左向右,从0开始) 对应 文件的fd值
  • 只要该位设置为1,就是我们想让内核关注该文件
  • eg:我们要关注0,1,2,3这四个文件:

输出时

  • 内核要告诉我们,关注的fd集中有哪些fd上的读事件已经就绪 + 返回的也是个位图结构
  • 所以,对应关系依然没有变,但代表的含义不同
  • 如果该位为1,说明该文件上的读事件已经就绪
  • 内核会先将位图清零,然后将[读事件已经就绪的文件]的fd值 对应的 比特位 置1
  • eg:四个文件中,fd=2的文件的读事件就绪:
总结 

所以,总结来说,fd_set这张位图,是让用户和内核之间互相传递信息

  • 那么,在使用select函数的过程中,一定会涉及大量的位图操作
fd_set操作接口 

为了让用户更方便,内核为我们提供了接口

timeout

设置select的等待方式

每隔若干秒,timeout一次,timeout后 / 有文件就绪后函数会返回

timevalue 结构体

在gettimeofday()中也有使用这个类型作为参数:

  • 获取特定时区下的特定时间,精确到微秒级别

  • 时间戳 -- 秒单位和微妙单位 
  • 比如传入参数{5,0},代表设置时间戳为5s
传入值
  • 设置>0 -- 每隔一段时间timeout一次,比如5s
  • 设置为0 -- 非阻塞(select立即返回)
  • 设置为NULL -- 阻塞等待,直到有文件就绪

如果设置(非NULL)了该时间

  • 则为输入输出型参数
  • 如果在等待的中途有文件就绪,则返回[timeout时间-已经等待时间],也就是[距离超时时间的剩余时间 ]

返回值

  • >0 -- 有n个fd就绪
  • =0 -- 超时,等待过程中没有错误,也没有fd就绪
  • <0 -- 等待出错(要等待的某个文件已经关闭了)

代码

我们这里实现一个非阻塞版网络通信

注意点 -- 调用函数

创建好套接字后,不能直接accept  

  • accept本质就是在检测并获取listensock上面的事件
  • 但我们这里目的就是要让select去等待事件(有事件了再去通知我们来获取,这时候调用accept就不会被阻塞了)
  • 所以不能先调用accept

这里的事件:

  • = 新连接到来 = 三次握手完成,系统把新连接投递到全连接队列里 = select里的读事件
  • 所以我们先调用select等待读事件

select的参数填充 

这里是服务器刚启动时,是我们需要让listensocket检测并获取新连接(新客户端与当前服务器通信)

  • 所以,等待的是listensocket上的读事件,并且当前只有这一个套接字
  • 所以,max_fd=listensocket_fd+1
  • 等有客户端连接后,会有新的套接字被创建(通信时使用的套接字),就需要添加检测这些套接字上的读写事件了(后面会细说)

因为timeout是输入输出型参数

  • 一旦超时/当前有事件就绪,就会修改timeout的值
  • 所以,为了不影响下一次的等待方式,需要重复设置timeout参数

三个位图集也是同理,需要重复设置

  • 不然会被修改成已经就绪的,而不代表需要内核关注的fd集

获取新连接

如果事件就绪,上层却不处理,select会一直通知

  • 所以需要我们手动调用accept()去把新连接拿走(这个操作在我们新的处理函数中)

当然,我们无法确定是哪个fd就绪了

  • 所以需要先判断
  • 判断完成后,就可以拿到新连接,创建新套接字了 

注意点 -- 通信时的调用函数

接下来要开始通信了,原先我们的服务器是直接read,但这里不行

  • 因为read是阻塞式等待,而我们要实现非阻塞式
  • 而且一旦阻塞在这里,就无法获取新连接以及与其他客户端通信了(因为我们写的是单进程)
  • 所以,还是需要使用select

添加新fd到位图中

当然,我们不能调用新的select

  • 为什么?
  • 一般都是在主循环处持续调用select,高效且简洁
  • 如果使用多个select,会导致代码逻辑复杂化,也难以管理

所以,需要我们把这个新套接字的fd设置进刚才的select的位图

  • ​​​​​​​这一过程就相当于d在不断增加自己鱼竿的数量

但是,这两个数据在不同的函数中(我们在处理函数中获取新连接,而select的使用在主逻辑函数中),如何传递呢?

  • 因为这两个函数都在类中,所以我们搞一个类内变量 -- 辅助数组
  • 让新增的fd都添加进辅助数组中,然后让select每次动态设置max_fd,以及三个位图

可以固定监听套接字(也就是我们创建的第一个套接字)作为数组的第一项

  • 方便我们后续区分[获取新连接] 和 [读写事件]

因为在过程中,可能会陆陆续续关掉一些文件

  • 所以原本添加进的连续fd,会变成零零星星的
  • 所以,需要我们每次都重新整理一下这个数组,把有效的fd统一放在左侧

我们每次在循环开头就处理数组中的值

  • 合法的fd就让它设置进位图中
  • 不仅如此,在这个过程中,我们还可以找到fd中的最大值,来填充select参数

解决了如何添加新fd的问题,接下来回到处理函数

处理函数

当我们识别到有事件就绪,获取连接后获得新套接字fd,之后就该将该fd设置进辅助数组中

  • 需要我们遍历数组,找到空位(值为-1/其他你设定的[数组内的初始值]),然后添加进去

更新ing...

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaScript使用高德API显示地图

前言 在JavaScript中&#xff0c;使用Leaflet库显示地图是一种常见的做法。Leaflet是一个开源的JavaScript库&#xff0c;用于在Web应用程序中创建互动地图。它非常轻量级&#xff0c;易于使用&#xff0c;并且提供了多种功能&#xff0c;使开发者能够轻松地将地图集成到他们的…

【Python报错已解决】 SyntaxError: positional argument follows keyword argument

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 前言一、问题描述1.1 报错示例1.2 报错分析1.3 解决思路 二、解决方法2.1 方法一&#xff1a;调整参数顺序2.2 步骤二…

银行贷款产品

1、对公贷款 1.1 一般贷款 按贷款期限可分为短期贷款和中长期贷款。短期贷款是指金融企业根据有关规定发放的,期限在1年(含1年)以下的各种贷款。中长期贷款是指金融企业发放的贷款期限在1年以上的各种贷款。 按提供方式不同可分为信用贷款、担保贷款等。信用贷款是指金融企…

springboot实战学习(1)(开发模式与环境)

目录 一、实战学习的引言 &#xff08;1&#xff09;前后端的大致学习模块 &#xff08;2&#xff09;后端 &#xff08;3&#xff09;前端 二、开发模式 一、实战学习的引言 &#xff08;1&#xff09;前后端的大致学习模块 &#xff08;2&#xff09;后端 Validation&#xf…

ubuntu上通过openvswitch卸载实现roce over vxlan

环境 操作系统&#xff1a; uname -a Linux 5.4.0-187-generic #207-Ubuntu SMP Mon Jun 10 08:16:10 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux Mellanox网卡&#xff1a; ethtool -i ens6np0 driver: mlx5_core version: 23.10-2.1.3 firmware-version: 20.39.3004 (MT_0…

在Linux下查看HBA卡的速率和状态

平时在Linux下映射存储&#xff0c;都是映射哪台就给哪台插线&#xff0c;然后在存储端扫描WWPN&#xff0c;简单粗暴&#xff0c;没技术含量。当然&#xff0c;光交下也可以看。 1&#xff0c;查看当前卡的品牌&#xff0c;常用的卡有两种&#xff0c;Emulex和Qlogic。 lspc…

C语言 - 预处理详解(一)#预定义符号 ##define #undef

文章目录 前言 一、预定义符号 二、#define (一)、#define 定义的标识符 (二)、#define 定义的宏 (三)、#define 替换规则 (四)、# 和 ## 1、 # 的作用 2、## 的作用 (五)、带副作用的宏参数 (六)、宏和函数的对比 (七)、命名约定 三、#undef 总结 前言 路漫漫其修远兮&#…

C语言深度剖析--不定期更新的第五弹

const关键字 来看一段代码&#xff1a; #include <stdio.h> int main() {int a 10;a 20;printf("%d\n", a);return 0; }运行结果如下&#xff1a; 接下来我们在上面的代码做小小的修改&#xff1a; #include <stdio.h> int main() {const int a 1…

【QT】基础入门学习

文章目录 浅析Qt应用程序的主函数使用qDebug()函数常用快捷键Qt 编码风格信号槽连接模型实现方案 信号和槽的工作机制Qt对象树机制 浅析Qt应用程序的主函数 #include "mywindow.h"#include <QApplication>// 程序的入口 int main(int argc, char *argv[]) {//…

IDA的安装和使用

IDA Pro&#xff08;简称 IDA&#xff0c;官网地址为 https://www.hex-rays.com/products/ida/&#xff09;是一个反编译器&#xff0c;同时具备调试器的功能。IDA Pro 的功能非常强大&#xff0c;几乎所有的逆向题目都需要用到它&#xff0c;因而也被称为「逆向神器」 IDA安装…

MySQL入门到精通

一、创建数据库 CREATE DATABASE 数据库名称; 如果数据库存在&#xff0c;则会提示报错。 二、选择数据库 USE 数据库名称; 三、创建数据表 CREATE TABLE 数据表名称; 四、MySQL数据类型 MySQL支持多种类型&#xff0c;大致可以分为三类&#xff1a;数值、日期/时间和字符串…

Data Filtering Network论文浅析

time2023-09paperhttps://arxiv.org/abs/2309.17425codehttps://huggingface.co/apple/DFN5B-CLIP-ViT-H-14-378org.Apple个人博客地址http://myhz0606.com/article/dfn Motivation 训练一个好的CLIP模型依赖大规模&#xff0c;高质量的训练数据。通过爬虫&#xff0c;可以很…

S7-1200与G120变频器CU240E-2控制单元通过353报文实现PN通信的基本方法

S7-1200与G120变频器CU240E-2控制单元通过353报文实现PN通信的基本方法 西门子报文353 PKW+PZD-2/2, 6个字中前4个字是PKW用,后2个字是PZD用, 结合以上内容, 可以知道第5个字是STW1控制字,第6个字是转速给定值(PLC输出);第5个字是ZSW1是状态字,第6个字是当前转速值(P…

LeetCode:快乐数(202)

目录 题目 代码思路 双指针 代码实现 题目 202. 快乐数 - 力扣&#xff08;LeetCode&#xff09; 编写一个算法来判断一个数 n 是不是快乐数。 [ 快乐数 ] 定义为&#xff1a; 对于一个正整数&#xff0c;每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程…

ThinkPHP5 5.0.23-rce远程代码执行漏洞复现

漏洞描述 ThinkPHP是一款运用极广的PHP开发框架。其版本5中&#xff0c;由于框架对控制器名没有进行足够的检测&#xff0c;会导致在没有开启强制路由的情况下可执行任意方法&#xff0c;从而导致远程命令执行漏洞。 启动容器 docker-compose up -d 查看端口 docker ps 端口为…

【C++进阶】hash表的封装

文章目录 hash表哈希表的关键组成部分哈希表的优缺点优点&#xff1a;缺点&#xff1a; 常见应用场景 开放定址法实现hash表负载因子 (Load Factor)负载因子的意义负载因子的影响再散列 (Rehashing)示例 整体框架insertFinderasehash桶封装框架insertfinderase~HashTable() 总结…

从路径优化学习FastPlanner之B样条曲线平滑(二):FastPlanner中B样条曲线代码理解与解读

参考别人的博客学习 根据之前一章只是大致了解了B样条数学原理&#xff0c;实际读代码还有疑惑。 控制点是什么&#xff1f;和规划出的路径点什么关系&#xff1f; 控制点可以说我们规划出的路径点&#xff0c;即n等于轨迹点个数。也可以不是轨迹点&#xff0c;通过线性方程反解…

Einsum(Einstein summation convention)

Einsum&#xff08;Einstein summation convention&#xff09; 笔记来源&#xff1a; Permute和Reshape嫌麻烦&#xff1f;einsum来帮忙&#xff01; The Einstein summation convention is a notational shorthand used in tensor calculus, particularly in the fields of …

免费也能高质量!2024年免费录屏软件深度对比评测

我公司因为客户覆盖面广的原因经常会开远程会议&#xff0c;有时候说的内容比较广需要引用多份的数据&#xff0c;我记录起来有一定难度&#xff0c;所以一般都用录屏工具来记录会议内容。这次我们来一起探索有什么免费录屏工具可以提高我们的工作效率吧。 1.福晰录屏大师 链…

9.7(QT.Day 1)

一、自由发挥登录窗口的应用场景&#xff0c;实现一个登录窗口界面 要求&#xff1a;每行代码都有注释 【需要用到的图片或者动图&#xff0c;自己去网上找】 1.mywidget.h代码 #ifndef MYWIDGET_H #define MYWIDGET_H#include <QWidget> #include <QIcon> //图…