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

本文是我的学习笔记,学习路线跟随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多路复用时检查该状态

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java LinkedList 实现栈和队列

Java LinkedList 实现栈和队列 package com.zhong.collection;import java.util.LinkedList;public class LinkedListDemo {public static void main(String[] args) {// LinkedList 创建一个队列LinkedList<String> queue new LinkedList<>();// 进队System.out…

Linux中断编程

大家好&#xff0c;今天给大家介绍Linux中断编程&#xff0c;文章末尾附有分享大家一个资料包&#xff0c;差不多150多G。里面学习内容、面经、项目都比较新也比较全&#xff01;可进群免费领取。 Linux中断编程涉及到操作系统层面的中断处理机制&#xff0c;它是Linux内核与硬…

基于FPGA的图像最近邻插值算法verilog实现,包括tb测试文件和MATLAB辅助验证

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 将FPGA数据导入matlab显示图片&#xff0c;效果如下&#xff1a; 2.算法运行软件版本 vivado2019.2&#xff0c;matlab2022a 3.部分核心程序 ti…

vue3 之 商城项目—详情页

整体认识 路由配置 准备组件模版 <script setup></script><template><div class"xtx-goods-page"><div class"container"><div class"bread-container"><el-breadcrumb separator">">&…

AI实景无人直播 矩阵系统

矩阵系统&#xff1a;重塑未来的组织与沟通在不断变化的世界中&#xff0c;我们需要的不仅是适应变化的能力&#xff0c;更需要预见未来的视角。矩阵系统&#xff0c;正是一个能够助力我们应对复杂环境、实现高效组织和沟通的工具。一、矩阵系统的核心价值矩阵系统&#xff0c;…

【05】C++ 内存管理

文章目录 &#x1f308; Ⅰ C 内存分布&#x1f308; Ⅱ C 内存管理方式1. new 和 delete 操作内置类型2. new 和 delete 操作自定义类型 &#x1f308; Ⅲ operator new 和 operator delete&#x1f308; Ⅳ new 和 delete 的实现原理1. 内置数据类型2. 自定义数据类型 &#…

Blazor SSR/WASM IDS/OIDC 单点登录授权实例1-建立和配置IDS身份验证服务

目录: OpenID 与 OAuth2 基础知识Blazor wasm Google 登录Blazor wasm Gitee 码云登录Blazor SSR/WASM IDS/OIDC 单点登录授权实例1-建立和配置IDS身份验证服务Blazor SSR/WASM IDS/OIDC 单点登录授权实例2-登录信息组件wasmBlazor SSR/WASM IDS/OIDC 单点登录授权实例3-服务端…

如何解锁屏幕破损的 iPhone

iPhone 15 是 Apple 最新、最出色的智能手机。它拥有时尚的设计、尖端的技术和众多功能&#xff0c;使其成为市场上最令人垂涎​​的设备之一。不幸的是&#xff0c;与所有智能手机一样&#xff0c;iPhone 14 容易发生可能导致屏幕破裂的事故和事故。破损的屏幕可能是毁灭性的&…

【机器学习】合成少数过采样技术 (SMOTE)处理不平衡数据(附代码)

1、简介 不平衡数据集是机器学习和人工智能中普遍存在的挑战。当一个类别中的样本数量明显超过另一类别时&#xff0c;机器学习模型往往会偏向大多数类别&#xff0c;从而导致性能不佳。 合成少数过采样技术 (SMOTE) 已成为解决数据不平衡问题的强大且广泛采用的解决方案。 …

2024刘谦春晚第二个扑克牌魔术

前言 就是刚才看春晚感觉这个很神奇&#xff0c;虽然第一个咱模仿不过来&#xff0c;第二个全国人民这么多人&#xff0c;包括全场观众都有成功&#xff0c;这肯定是不需要什么技术&#xff0c;那我觉得这个肯定就是数学了&#xff0c;于是我就胡乱分析一通。 正文 首先准备…

C语言:分支与循环

创造不易&#xff0c;友友们给个三连吧&#xff01;&#xff01; C语⾔是结构化的程序设计语⾔&#xff0c;这⾥的结构指的是顺序结构、选择结构、循环结构&#xff0c;C语⾔是能够实 现这三种结构的&#xff0c;其实我们如果仔细分析&#xff0c;我们⽇常所⻅的事情都可以拆分…

[C/C++] -- Boost库、Muduo库编译安装使用

1.Muduo库 Muduo 是一个基于 C11 的高性能网络库&#xff0c;其核心是事件驱动、非阻塞 I/O、线程池等技术&#xff0c;以实现高并发、高性能的网络通信。Muduo 库主要由陈硕先生开发维护&#xff0c;已经成为 C 服务器程序员的常用工具之一。 Muduo 库的主要特点&#xff1a…

(每日持续更新)jdk api之ObjectInputStream基础、应用、实战

博主18年的互联网软件开发经验&#xff0c;从一名程序员小白逐步成为了一名架构师&#xff0c;我想通过平台将经验分享给大家&#xff0c;因此博主每天会在各个大牛网站点赞量超高的博客等寻找该技术栈的资料结合自己的经验&#xff0c;晚上进行用心精简、整理、总结、定稿&…

Vulnhub靶机:hacksudo-Thor

一、介绍 运行环境&#xff1a;Virtualbox 攻击机&#xff1a;kali&#xff08;10.0.2.15&#xff09; 靶机&#xff1a;hacksudo-Thor&#xff08;10.0.2.49&#xff09; 目标&#xff1a;获取靶机root权限和flag 靶机下载地址&#xff1a;https://download.vulnhub.com/…

[每周一更]-(第86期):PostgreSQL入门学习和对比MySQL

入门学习PostgreSQL可以遵循以下步骤&#xff1a; 安装 PostgreSQL&#xff1a; 首先&#xff0c;你需要在你的计算机上安装 PostgreSQL。你可以从 PostgreSQL 官方网站 下载适合你操作系统的安装包&#xff0c;并按照官方文档的指导进行安装。 学习 SQL&#xff1a; PostgreS…

【动态规划】【C++算法】LeetCoce996正方形数组的数目

作者推荐 【动态规划】【前缀和】【C算法】LCP 57. 打地鼠 本文涉及知识点 动态规划汇总 LeetCoce996正方形数组的数目 给定一个非负整数数组 A&#xff0c;如果该数组每对相邻元素之和是一个完全平方数&#xff0c;则称这一数组为正方形数组。 返回 A 的正方形排列的数目…

Electron基本介绍

Electron基本介绍 Electron 官方网站&#xff1a;https://www.electronjs.org/zh/ Electron安装方法&#xff1a;npm install electron -g 全局安装 Electron简介&#xff1a;Electron提供了丰富的本地&#xff08;操作系统&#xff09;API&#xff0c;使你能够使用纯JavaScr…

图解 V8 执行 JS 的过程

本文来分享 V8 引擎执行 JavaScript 的过程 1. JS 代码执行过程 在说V8的执行JavaScript代码的机制之前&#xff0c;我们先来看看编译型和解释型语言的区别。 编译型语言和解释型语言 我们知道&#xff0c;机器是不能直接理解代码的。所以&#xff0c;在执行程序之前&#xf…

3.1 Verilog 连续赋值

关键词&#xff1a;assign&#xff0c; 全加器 连续赋值语句是 Verilog 数据流建模的基本语句&#xff0c;用于对 wire 型变量进行赋值。&#xff1a; 格式如下 assign LHS_target RHS_expression &#xff1b; LHS&#xff08;left hand side&#xff09; 指赋值操作…

系统架构24 - 软件架构设计(3)

软件架构风格&#xff08;上&#xff09; 概述架构风格数据流架构风格批处理风格管道-过滤风格 调用/返回架构风格主程序/子程序风格面向对象风格层次结构风格客户端/服务器风格 以数据为中心的架构风格仓库风格黑板风格 虚拟机架构风格解释器风格规则系统风格 独立构件架构风格…