C++Linux网络编程day02:select模型

news2024/9/22 12:29:10

本文是我的学习笔记,学习路线跟随Github开源项目,链接地址:30dayMakeCppServer

文章目录

    • select模型
        • fd_set结构体
      • timeval结构体
      • 文件描述符的就绪条件
      • 带外数据与普通数据
      • socket的状态

select模型

select是Linux下的一个IO复用模型,同时,它也是Linux中一个系统函数的名称:

#include <sys/select.h>

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

select系统函数的用途是:

在一段指定时间内,监听用户感兴趣的文件描述符上的可读、可写和异常等事件

我们先对这个函数的各个参数进行一个解释:

  • ndfs:指定被监听的文件描述符的总数
  • resdfds、writefds、exceptfds:分别指向可读、可写和异常事件所对应的文件描述符
  • timeout:设置时间,若是NULL将会一直阻塞,知道某个文件描述符就绪

select成功时返回[[#文件描述符的就绪状态|就绪(可读、可写和异常)文件描述符]]的总数。如果在超时时间内没有任何文件描述符就绪,select将返回0。
select失败时返回-1并设置errno。
如果在程序等待期间,程序收到信号(例如Ctrl+C这种,可由函数kill发起,这点在Linux系统编程中有说到),则select会立即返回-1,并设置errno为EINTER(这是在errno.h中定义的一个错误代码,意思是:“Interrupted system call”,即:系统调用被中断

fd_set结构体

fd_set结构体的实质其实是:一个存放文件描述符状态的数组,它的定义类似于以下结构:

typedef struct {
    long fds_bits[FD_SETSIZE / (8 * sizeof(long))];
} fd_set;

这个数组的长度由FD_SETSIZE决定。现在,我们对这个数组进行一个更详细的解释。
这里给出fd_set的完整定义:

#include <typesizes.h>
/*
	文件描述符的最大数量
*/
#define __FD_SETSIZE 1024
#include <sys/select.h>
// 这个好像没啥用
#define FD_SETSIZE__FDFDSETSIZE
/*
	给long int取个别名叫__fd_mask
	这个类型的占用未4或8字节,具体看编译器和架构
*/
typedef long int __fd_mask;
// 取消宏定义,可能是为了防止宏定义冲突
#undef __NFDBITS
/*
	重新设定__NFDBITS
	————NFDBITS计算的是__fd_maks所占的位数
*/
#define __NFDBITS (8*(int)sizeof(__fd_mask))
typedef struct{
	#ifdef __USE_XOPEN
	__fd_mask fds_bits[__FD_SETSIZE/__NFDBITS]
	#define __FDS_BITS(set((set)->fds_bits))
	#else
	__fd_mask fd_bits[__FD_SETSIZE/__NFDBITS];
	#define __FDS_BITS(set)((set)->__fds_bits)
	#endif
}fd_set

从上面这段代码可以知道:fd_set数组所占用的bit的数量其实就是__FD_SETSIZE/8
这是因为:fd_set的本质是位图,即:它不直接存储文件描述符,而是根据其中每一位的存储情况来确定某一文件描述符的状态。在这里插入图片描述

具体如下:

  • 位图的索引(数组的下标)对应文件描述符的编号,即第0位对应文件描述符0,第1位对应文件描述符1,以此类推
  • 位图的值表示对应的文件描述符的状态。如果位值位1,表示该文件描述符需要监视;若是0,则表示该文件描述符不需要监视

由于fd_set中的操作本质上是位操作,我们想要进行操作会十分的复杂,因此我们应当使用其提供的一系列宏来访问fd_set中的位

#include <sys/select.h>
/*
	清除fdset中的所有位
*/
FD_ZERO(fd_set* fdset);
/*
	设置fd_set中的位fd
*/
FD_ZERO(int fd, fd_set* fdset);
/*
	清除fdset中的位fd
*/
FD_CLR(int fd, fd_set* fdset);
/*
	测试fdset的位fd是否被设置
*/
int FD_ISSET(int fd, fd_set* fdset);

timeval结构体

在上面的select系统函数中,最后一个参数就是一个timeval结构体指针,它用于设置select的超时时间:

struct timeval{
	long tv_sec; // 秒数
	long tv_usec;// 微秒数
};

从其定义我们可以看出:它提供了微秒级的定时方式,若是我们将其设置为0,即:监听时间为0,这会使得select立即返回。
至于为什么需要使用指针呢?这点应该很好理解,就是为了避免拷贝,需要将该参数提交给内核,内核会将其修改,以告诉应用程序select等待了多久
但是在游双的《Linux高性能服务器编程》中写道:

不过,我们不能完全信任select调用返回后的timeout值,比如调用失败时timeout值是不确定的。

文件描述符的就绪条件

此节摘抄与游双的《Linux高性能服务器编程》第九章,具体为9.1.2
在网络编程中,下列情况的socket可读:

  • socket内核接收缓存区中的字节数大于或等于低水位标记SO_RCVLOWAT。此时我们可以无阻塞地读取该socket,并且读操作返回地字节数大于0
  • socket通信地对方关闭连接。此时对该socket读操作将返回0
  • 监听socket上有新的连接请求
  • socket上有未处理的错误。此时我们可以使用getsockopt来读取和清除该错误

下列情况下socket可写:

  • socket内核发送缓存区中的可用字节数大于或等于其低水位标记SO_SNDLOWAT。此时我们可以无阻塞地写该socket,并且写操作返回的字节数大于0
  • socket的写操作被关闭。对写操作被关闭的socket执行写操作将触发一个SIGPIPE信号
  • wocket使用非阻塞的connect连接成功或者失败(超时)之后
  • socket上有未处理的错误。此时我们可以使用getsockopt来读取和清除该错误

在网络程序中,select能处理的异常情况只有一种:socket上接收到[[#带外数据与普通数据|带外数据]]

带外数据与普通数据

普通数据(Normal Data)是指正常的、按照通常顺序传输的数据是正常状态。当进行网络通信时,普通数据是按照先进先出的原则进行传输的。发送方将数据逐个字节地发送给接收方,并确保接收方按照相同的顺序接收和重新组装数据。
带外数据(Out-of-Band Data)指的是具有高优先级的数据,被划分为与普通数据分开的数据通道,它也被称为”紧急数据“,是一种异常状态带外数据可以在数据流中插入,即使在普通数据还未发送完毕的情况下,带外数据也可以及时传输并被接收方处理。带外数据的传输方式通常比普通数据的传输优先级更高。
带外数据通常用于发送一些紧急的控制信息或异常情况的通知,例如中断信号或连接关闭请求等。它们被用于提供紧急服务或在遇到特定事件时发送重要的控制信息。

socket的状态

在常见的套接字模型中,套接字的状态可分为以下几种:

  • 未连接(unconnected):套接字没有与对方建立连接,处于初始状态或者已关闭状态
  • 监听(listening):服务器套接字正在等待客户端连接请求。这通常用于服务端创建一个监听套接字,以接收客户端的连接请求
  • 连接已建立(connected):套接字成功与远程对等体建立连接,并可以进行数据传输
  • 关闭等待(closing):套接字已发送关闭请求,正在等待对方的相应或确认关闭
  • 已关闭(closed):套接字连接已经关闭,并且不再可用(不可再次连接)

除了以上套接字状态,套接字在某一时刻拥有不同的状态指示符

  • 可读(readable):套接字上有数据可供读取。可以使用select等异步IO多路复用时检查该状态
  • 可写(writable):套接字上可以写入数据。可以使用select等异步IO多路复用时检查该状态
  • 异常(exceptional):表示套接字遇到异常情况,例如:带外数据到达或者发生错误等。可以使用select等异步IO多路复用时检查该状态

这些状态指示符是用于通知应用程序在某一时刻的特定状态,以进行相应的处理。

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

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

相关文章

用HTML5实现灯笼效果

本文介绍了两种实现效果&#xff1a;一种使用画布&#xff08;canvas&#xff09;标签/元素&#xff0c;另一种不用画布&#xff08;canvas&#xff09;标签/元素主要使用CSS实现。 使用画布&#xff08;canvas&#xff09;标签/元素实现&#xff0c;下面&#xff0c;在画布上…

Idea里自定义封装数据警告解决 Spring Boot Configuration Annotation Processor not configured

我们自定对象封装指定数据&#xff0c;封装类上面一个红色警告&#xff0c;虽然不影响我们的执行&#xff0c;但是有强迫症看着不舒服&#xff0c; 去除方式&#xff1a; 在pom文件加上坐标刷新 <dependency><groupId>org.springframework.boot</groupId><…

Android AOSP源码研究之万事开头难----经验教训记录

文章目录 1.概述2.Android源下载1.配置环境变量2.安装curl3.下载repo并授权4.创建一个文件夹保存源码5.设置repo的地址并配置为清华源6.初始化仓库7.指定我们需要下载的源码分支并初始化 2.1 使用移动硬盘存放Android源码的坑2.2 解决方法 3.Android源码编译4.Android源烧录 1.…

v-if 和v-for的联合规则及示例

第073个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下&#xff0c;本专栏提供行之有效的源代码示例和信息点介绍&#xff0c;做到灵活运用。 提供vue2的一些基本操作&#xff1a;安装、引用&#xff0c;模板使用&#xff0c;computed&a…

SpringBoot WebSocket客户端与服务端一对一收发信息

依赖 <!--websocket--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>配置类 Configuration public class WebSocketConfig {Bean //方法返回值交…

跟着小德学C++之TCP基础

嗨&#xff0c;大家好&#xff0c;我是出生在达纳苏斯的一名德鲁伊&#xff0c;我是要立志成为海贼王&#xff0c;啊不&#xff0c;是立志成为科学家的德鲁伊。最近&#xff0c;我发现我们所处的世界是一个虚拟的世界&#xff0c;并由此开始&#xff0c;我展开了对我们这个世界…

FATFA文件系统

一.文件系统基本知识 1.文件系统是什么&#xff1f; 文件系统是一种用于组织和存储计算机上的文件和目录的方法。它是操作系统中的一个重要组成部分&#xff0c;负责管理磁盘或其他存储介质上的文件&#xff0c;使其易于访问和使用。文件系统提供了一种结构化的方式来组织文件…

three.js 匀速动画(向量表示速度)

效果&#xff1a; 代码&#xff1a; <template><div><el-container><el-main><div class"box-card-left"><div id"threejs" style"border: 1px solid red"></div>1. 匀速动画(向量表示速度)</div…

RibbonOpenFeign源码(待完善)

Ribbon流程图 OpenFeign流程图

Window环境下使用go编译grpc最新教程

网上的grpc教程都或多或少有些老或者有些问题&#xff0c;导致最后执行生成文件时会报很多错。这里给出个人实践出可执行的编译命令与碰到的报错与解决方法。&#xff08;ps:本文代码按照煎鱼的教程编写&#xff1a;4.2 gRPC Client and Server - 跟煎鱼学 Go (gitbook.io)&…

R语言rmarkdown使用

1、安装 install.packages(rmarkdown) library(rmarkdown) install.packages(tinytex) tinytex::install_tinytex() 2、新建R Markdown 3、基本框架 红色框内为YAML&#xff1a;包括标题、作者和日期等 黄色框内为代码块&#xff1a;执行后面的代码&#xff0c;并可以设置展…

Java风暴:打造高效作家信息管理平台

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

Spring Boot + 七牛OSS: 简化云存储集成

引言 Spring Boot 是一个非常流行的、快速搭建应用的框架&#xff0c;它无需大量的配置即可运行起来&#xff0c;而七牛云OSS提供了稳定高效的云端对象存储服务。利用两者的优势&#xff0c;可以为应用提供强大的文件存储功能。 为什么选择七牛云OSS? 七牛云OSS提供了高速的…

NGINX upstream、stream、四/七层负载均衡以及案例示例

文章目录 前言1. 四/七层负载均衡1.1 开放式系统互联模型 —— OSI1.2 四/七层负载均衡 2. Nginx七层负载均衡2.1 upstream指令2.2 server指令和负载均衡状态与策略2.2.1 负载均衡状态2.2.2 负载均衡策略 2.3 案例 3. Nginx四层负载均衡的指令3.1 stream3.2 upstream指令3.3 四…

排序算法---归并排序

原创不易&#xff0c;转载请注明出处。欢迎点赞收藏~ 归并排序是一种常见的排序算法&#xff0c;它采用了分治的思想。它将一个待排序的数组递归地分成两个子数组&#xff0c;分别对两个子数组进行排序&#xff0c;然后将排好序的子数组合并成一个有序数组。 具体的归并排序过…

Docker部署前端项目

某次阿里云的自动流水线失败了&#xff0c;代码本地跑起来莫得问题&#xff0c;错误日志提示让我跑一下npm run build &#xff0c;但是俺忽然发现&#xff0c;我跑了&#xff0c;文件打包好了&#xff0c;但是往哪里运行呢&#xff1f;这涉及到要构建一个环境供打包文件部署吧…

Git的基础操作指令

目录 1 前言 2 指令 2.1 git init 2.2 touch xxx 2.3 git status 2.4 git add xxx 2.5 git commit -m xxxx 2.5 git log及git log --prettyoneline --all --graph --abbrev-commit 2.6 rm xxx 2.7 git reset --hard xxx(含小技巧) 2.8 git reflog 2.9 mv xxx yyy 1…

计算机考研数学】张宇1000题和660哪个更难?

1000题和660题都很难&#xff0c;难的不一样 660题是对于基础深入考察的难 660题是非常经典的客观题练习题&#xff0c;题目难度中等&#xff0c;不难但是每一道题都需要认真的思考才能做出来。如果660题能够吃透&#xff0c;并且每一道题的方法都能够灵活掌握的话&#xff0…

Python Paramiko 使用交互方式获取终端输出报错

近期接到一个需求&#xff0c;要批量登录网络设备获取配置。 原计划使用 Paramiko exec即可&#xff0c;但是后来发现&#xff0c;有些设备命令也执行了&#xff0c;但是没有回显。 于是尝试使用 invoke_shell() 方式。 前期调试倒是OK&#xff0c;直到遇见一个输出内容较长的…

python-pandas查漏补缺

1. create labels for Series 2. 3. 4. 用平均数等去填empty的格子 5. 6. 7.