【linux高级IO(二)】多路转接之select详解

💓博主CSDN主页:杭电码农-NEO💓

⏩专栏分类:Linux从入门到精通⏪

🚚代码仓库:NEO的学习日记🚚

🌹关注我🫵带你学更多操作系统知识
  🔝🔝


Linux高级IO

  • 1. 前言
  • 2. 初识select
  • 3. 理解select的执行过程
  • 4. select的简使用示例
  • 5. 总结

1. 前言

多路转接一共有三种实现方案, 分别是select,poll和epoll. 本系列文章会一一讲解它们的优缺点和使用方法. 如果你还不清楚这些IO模型, 请先阅读这篇文章: Linux高级IO

本章重点:

本篇文章着重讲解select函数的原型和用法, 并且会带大家实现一个简单的select多路转接代码


2. 初识select

系统提供select函数来实现多路复用输入/输出模型.

select函数的原型:

在这里插入图片描述

参数解释:

  • 参数nfds是需要监视的最大的文件描述符值+1;
  • rdset,wrset,exset分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集合及异常文件描述符的集合;
  • 参数timeout为结构timeval,用来设置select()的等待时间

就绪事件通常分为可读事件,可写事件和异常事件

参数timeout的意义:

  • NULL:则表示select()没有timeout,select将一直被阻塞,直到某个文件描述符上发生了事件;
  • 0:仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生。
  • 特定的时间值:如果在指定的时间段里没有事件发生,select将超时返回。

select函数参数中, 使用了fd_set类型变量, 如果你对信号部分比较熟悉, 那么你一定能猜到, fd_set是个位图, 你可以把你需要关心的所有fd都设置到这个位图中, 让select函数帮你关心, 比如你想要关心0号文件描述符(stdin)的读事件, 那么就在readfd中将0设置进去.

在这里插入图片描述
select函数的返回值以及错误码:

在这里插入图片描述


3. 理解select的执行过程

timel结构体参数,fd_set类型参数都是输入输出型参数

  • timeval类型参数: 假如你设置阻塞时间为5秒, 但是等待了三秒后就有事件就绪, 函数就返回了, 那么timeval类型参数的值会被设置成为2秒.
  • fd_set类型参数, 输入你想要关心的fd集合, 输出时, 此结构中存放, 已经事件就绪的fd集合. 比如你想要关心0~10号文件描述符的读事件, 函数返回时, 此集合中可能只有1.3.5号fd被返回了, 也就是说只有1.3.5号fd的事件就绪了

综上所述: 每一次调用完select后,都需要我们重置参数

while(1)
{//处理完重置参数fs_set readset;FD_SET(fd,&readset);select(fd+1,&readset,NULL,NULL,NULL);if(FD_ISSET(fd,readset)){……}
}

什么叫做事件就绪?拿读事件来说, 客户端和服务器建立连接后, 客户端就一定会向服务器发送数据吗? 不一定! 所以服务器只能等待客户端发数据, 一旦接收缓冲区有数据到来, 那么就是读事件就绪了, 可以直接调用recv函数从对应的fd中将数据拿到内存. 写事件也是如此, 可能此时的发送缓冲区已满, 那么就需要等待

select的特点:

在这里插入图片描述

fd_set的大小可变,有兴趣可自行查资料

select的缺点:

在这里插入图片描述

正因为select的这些缺陷, 才会有后面的poll和epoll来代替它


4. select的简使用示例

多路转接的编程本质上也是网络编程, 所以我们可以先定义一个sock.h文件用于网络通信的套接字编程:

class Sock
{const static int gbacklog = 20;
public:Sock(){}static int Socket(){}static void Bind(int sock, uint16_t port, string ip = "0.0.0.0"){}static void Listen(int sock){}static int Accept(int listensock,string* ip,uint16_t* port){}   static bool Connect(int sock,string serverip,uint16_t serverport)//客户端要连接谁就传谁的ip和port{}~Sock(){}
};

接下来是select的编程:

#ifndef __SELECT_SVR_H__
#define __SELECT_SVR_H__
#include <iostream>
#include <sys/select.h>
#include <vector>
#include "log.hpp"
#include "sock.hpp"
#include <string>
#include <sys/time.h>
using namespace std;#define BITS 8
#define NUM (sizeof(fd_set) * BITS)
#define FD_NONE -1// 只完成读取,写入和异常暂时不做处理,单进程,可以同时为多个人服务
class Select_Server
{
public:Select_Server(uint16_t port = 9090): _port(port){_listensock = Sock::Socket();Sock::Bind(_listensock, port);Sock::Listen(_listensock);logMessage(DEBUG, "创建基础套接字成功!");for (int i = 0; i < NUM; i++)_array[i] = FD_NONE; // 规定array[0] = _listensock_array[0] = _listensock;}void Start(){while (1){DebugPrint();// struct timeval timeout = {5, 0};//  如何看待当前唯一的套接字?获取新连接,我们把它依旧看作IO,input事件,如果没有连接到来,调用accept就会阻塞,不能直接调用accept//  FD_SET(_listensock, &readfd); // 将listensock添加到文件描述符集合中//  int n = select(_listensock + 1, &readfd, nullptr, nullptr, &timeout);//  随着获取的sock越来越多,注定了nfds每一次都可能要发送变化,需要对它进行动态计算,并且rfds,writefds是输入输出型参数,每次输入输出可能不同,注定了每次都要对rfds进行重新添加//  应该将自己所有的文件描述符都单独保存起来,用来支持: 1. 更新最大值 2. 更新位图结构fd_set readfd;FD_ZERO(&readfd); // 将这个位图清空int maxfd = _listensock;for (int i = 0; i < NUM; i++) // 更新位图,寻找最大值{if (_array[i] == FD_NONE)continue;FD_SET(_array[i], &readfd);if (_array[i] > maxfd)maxfd = _array[i];}int n = select(maxfd + 1, &readfd, nullptr, nullptr, nullptr);if (n == 0) // 没有文件描述符就绪//logMessage(DEBUG, "time out");else if (n == -1)//logMessage(DEBUG, "select error");else{//logMessage(DEBUG, "select获取到一个链接");HandlerEvent(readfd);}}}~Select_Server(){if (_listensock >= 0)close(_listensock);}private:void Accepter(){}void Recver(int sock, int pos){}void HandlerEvent(const fd_set &readfd) // fd_set是一个集合,里面可能存在多个sock{}
private:uint16_t _port;int _listensock;int _array[NUM];
};
#endif

关于代码的解释都在注释中, 如果你想查看完整的代码, 可以在gitee: select编程示例中找到你想要的一切

需要注意的是, 读事件就绪有两种情况, 一种是新来fd的连接了, 另外一种是已有的连接的数据就绪了, 所以这两种情况需要分开讨论


5. 总结

为什么多路转接在实际生活中运用如此之多? 答案就是它一次性可以等待多个文件描述符, 效率很高. 但是select多路转接方案有局限性, 所以后面的epoll才是学习的重点


🔎 下期预告:多路转接之poll 🔍

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

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

相关文章

TEE共享内存的介绍-共享内存的限制是什么? TA堆内存/栈内存有限制吗?Trustzone/TEE

快速链接: . 👉👉👉 个人博客笔记导读目录(全部) 👈👈👈 付费专栏-付费课程 【购买须知】:【精选】TEE从入门到精通-[目录] 👈👈👈思考: 如何开发一个TA? sdk又是什么?开发一个TA的流程是怎样的?How to do?有关TA的签名介绍TEE开发Secure driver介绍RP…

昇思25天学习打卡营第21天|基于MindSpore实现BERT对话情绪识别

模型简介 BERT全称是来自变换器的双向编码器表征量。与BERT模型相似的预训练语言模型例如问答、命名实体识别、自然语言推理、文本分类等在许多自然语言处理任务中发挥着重要作用。模型是基于Transformer中的Encoder并加上双向的结构。 BERT模型的主要创新点都在pre-train方法…

SCI丨返修一作+通讯

中科四区&#xff0c;JCR2 返修转让一作通讯&#xff0c;5个月左右录用 题目&#xff1a;通过机器学习算法XXXXXXXxxx混凝土力学性能的可靠方法

Nginx 配置ssl证书

1. 准备 SSL 证书文件 确保您有以下文件&#xff1a; SSL 证书文件&#xff08;通常是 .crt 或 .pem 文件&#xff09; 私钥文件&#xff08;通常是 .key 文件&#xff09; 中间证书文件&#xff08;如果适用&#xff0c;通常是 .crt 或 .pem 文件&#xff09; 将这些文件上传…

苍穹外卖--完善登录功能:进行MD5加密

目标 TODO&#xff1a;使用MD5加密方式对明文密码。 实现 password DigestUtils.md5DigestAsHex(password.getBytes());

Face_recognition实现人脸识别

这里写自定义目录标题 欢迎使用Markdown编辑器一、安装人脸识别库face_recognition1.1 安装cmake1.2 安装dlib库1.3 安装face_recognition 二、3个常用的人脸识别案例2.1 识别并绘制人脸框2.2 提取并绘制人脸关键点2.3 人脸匹配及标注 欢迎使用Markdown编辑器 本文基于face_re…

双向链表+Map实现LRU

LRU: LRU是Least Recently Used的缩写&#xff0c;即最近最少使用&#xff0c;是一种常用的页面置换算法&#xff0c;选择最近最久未使用的页面予以淘汰。 核心思想&#xff1a; 基于Map实现k-v存储&#xff0c;双向链表中使用一个虚拟头部和虚拟尾部&#xff0c;虚拟头部的…

BioXcell—InVivoMAb anti-West Nile/dengue virus E protein

研发背景&#xff1a; 西尼罗河病毒(WNV)是一种由蚊虫类介导传播的黄病毒&#xff0c;与引起人类感染性流行病的登革热病毒、黄热病病毒和日本脑炎病毒密切相关。 WNV和登革热病毒(DENV)同属黄病毒科&#xff08;Flaviviridae&#xff09;黄热病毒属&#xff0c;是具有小包膜单…

实现llava的【单轮对话】调整成【多轮对话】 (输入图片/多模态/多轮对话/llava)

使用llava时&#xff0c;将llava的单轮对话调整成多轮对话 先说好方法一&#xff0c;直接对官方网站的quick start代码进行修改方法二&#xff1a;使用bash结合基础的单轮对话代码进行修改2.1 首先是最基础的llava官网代码回顾2.2 伪多轮对话方法&#xff08;每次新的prompt输入…

AEC10 SA计算整理 --- 基础SA

LuxSA: LuxSALumaAvgLumaBE16x16 LuxSATarget[setparam/tr:luxlux] LuxSAAdjRatioLuxSATarget/LuxSALumaLuxSALuma: 计算16x16区域的平均亮度&#xff08;Luma值&#xff09;。 LuxSATarget: 通过参数设置获取目标亮度值&#xff08;通常与当前光线条件相关&#xff09;。 Lux…

【多模态】41、VILA | 打破常规多模态模型训练策略,在预训练阶段就微调 LLM 被证明能取得更好的效果!

论文&#xff1a;VILA: On Pre-training for Visual Language Models 代码&#xff1a;https://github.com/NVlabs/VILA 出处&#xff1a;NVLabs 时间&#xff1a;2024.05 贡献&#xff1a; 证明在预训练阶段对 LLM 进行微调能够提升模型对上下文任务的效果在 SFT 阶段混合…

同三维T80006解码器视频使用操作说明书:高清HDMI解码器,高清SDI解码器,4K超清HDMI解码器,双路4K超高清解码器

同三维T80006解码器视频使用操作说明书&#xff1a;高清HDMI解码器&#xff0c;高清SDI解码器&#xff0c;4K超清HDMI解码器&#xff0c;双路4K超高清解码器 解码器T80006 同三维&#xff0c;十多年老品牌&#xff0c;我们一直专注&#xff1a;视频采集卡、视频编码器、转码器、…

Centos7离线安装ElasticSearch7.4.2

一、官网下载相关的安装包 ElasticSearch7.4.2&#xff1a; elasticsearch-7.4.2-linux-x86_64.tar.gz 下载中文分词器&#xff1a; elasticsearch-analysis-ik-7.4.2.zip 二、上传解压文件到服务器 上传到目录&#xff1a;/home/data/elasticsearch 解压文件&#xff1…

免费无限白嫖阿里云服务器

今天&#xff0c;我来分享一个免费且无限使用阿里云服务器的方法&#xff0c;零成本&#xff01;这适用于日常测试学习&#xff0c;比如测试 Shell 脚本、学习 Docker 安装、MySQL 等等。跟着我的步骤&#xff0c;你将轻松拥有一个稳定可靠的服务器&#xff0c;为你的学习和实践…

数据库的优点和缺点分别是什么

数据库作为数据存储和管理的核心组件&#xff0c;具有一系列显著的优点&#xff0c;同时也存在一些潜在的缺点。以下是对数据库优点和缺点的详细分析&#xff1a; 优点 数据一致性&#xff1a;数据库通过事务处理和锁机制等手段&#xff0c;确保数据的一致性和完整性。这意味着…

错误记录-SpringCloud-OpenFeign测试远程调用

文章目录 1&#xff0c;org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name memberController: Unsatisfied dependency expressed through field couponFeign2&#xff0c; Receiver class org.springframework.cloud.netflix…

几种不同的方式禁止IP访问网站(PHP、Nginx、Apache设置方法)

1、PHP禁止IP和IP段访问 <?//禁止某个IP$banned_ip array ("127.0.0.1",//"119.6.20.66","192.168.1.4");if ( in_array( getenv("REMOTE_ADDR"), $banned_ip ) ){die ("您的IP禁止访问&#xff01;");}//禁止某个IP段…

SAP S4 环境下,KSU1 Ob52 转为前台操作,不产生传输请求号

参考 OB52/KSV1/KSU1等与Client状态相关的前台操作-y_q_yang se16n 编辑表 T811FLAGS 新增行 CYCLES MAINTENANCE X 即可

WAIC 2024 AI盛宴大会亮点回顾

&#x1f389; 刚刚落幕的WAIC 2024大会&#xff0c;简直是科技迷们的狂欢节&#xff01;这场全球瞩目的人工智能盛会&#xff0c;不仅汇聚了全球顶尖的智慧&#xff0c;还带来了无数令人惊叹的创新成果。让我们一起回顾那些抓人眼球的亮点吧&#xff01; &#x1f525; 超燃开…

Java面试八股之MySQL的redo log和undo log

MySQL的redo log和undo log 在MySQL的InnoDB存储引擎中&#xff0c;redo log和undo log是两种重要的日志&#xff0c;它们各自服务于不同的目的&#xff0c;对数据库的事务处理和恢复机制至关重要。 Redo Log&#xff08;重做日志&#xff09; 功能 redo log的主要作用是确…