网络I/O模型
网络I/O模型
- 同步I/O
- 阻塞I/O
- 非阻塞I/O
- I/O多路复用
- select
- 函数接口
- 示例
- poll
- 函数接口
- 示例
- poll 和 select 的区别
- epoll
- 原理:
- 示例
- 异步I/O
同步I/O
阻塞I/O

一个基本的C/S模型如下图所图:其中 listen()、connect()、write()、read() 都是阻塞I/O,用户态的进程在执行这些操作时会陷入内核态并被挂起。直到 socket() 上有数据可读/写时,进程才会从内核态切换到用户态,并完成数据在内核态和用户态之间的复制。

int fd = open("file.txt", O_RDONLY);
char buffer[100];
ssize_t bytes_read = read(fd, buffer, sizeof(buffer)); // 阻塞
close(fd);
非阻塞I/O

I/O多路复用
select
select() 函数是一个用于多路复用I/O的系统调用,允许程序同时监控多个文件描述符(如套接字、管道、终端等)的可读、可写或异常状态。
函数接口
#include <sys/select.h>int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
其中:
- nfds: 整型变量,表示在所有集合中最大的文件描述符加1。这是为了告诉select()需要检查的文件描述符范围。实际上,你只需要提供所有被监控的文件描述符中的最大值加1即可。
- readfds: 指向fd_set结构的指针,用于存放待检查可读性的文件描述符集合。如果某个文件描述符在此集合中并且变为可读,select()返回后,该文件描述符在集合中的状态仍会被保持为已设置。
- writefds: 用于存放待检查可写性的文件描述符集合。
- exceptfds: 用于存放待检查异常条件的文件描述符集合。
- timeout: 指向timeval结构的指针,用来指定select()调用的最大等待时间。如果为NULL,select()将一直阻塞直到有文件描述符满足条件;如果不为NULL且值为(0, 0),则select()会立即返回,不进行等待;如果指定了具体的时间,则select()最多等待指定的时间。
返回值
-
正值: 返回的是已就绪的文件描述符的数量(包括可读、可写或异常的)。注意,这个值并不直接告诉你哪些描述符就绪,你需要通过FD_ISSET宏检查集合来确定具体哪些描述符准备好了。
-
0: 表示在指定的超时时间内没有文件描述符变为就绪状态。
-
-1: 表示调用出错,此时可以查看errno来获取具体的错误信息。
示例
void updateReadSet(std::unordered_set<int> &clientFds, int &maxFd, int sockFd, fd_set &readSet) {maxFd = sockFd;FD_ZERO(&readSet);FD_SET(sockFd, &readSet);for (const auto &clientFd : clientFds) {if (clientFd > maxFd) {maxFd = clientFd;}FD_SET(clientFd, &readSet);}
}void handlerClient(int clientFd) {std::string msg;if (not EchoServer::RecvMsg(clientFd, msg)) {return;}EchoServer::SendMsg(clientFd, msg);
}int main(int argc, char *argv[]) {if (argc != 3) {std::cout << "invalid input" << std::endl;std::cout << "example: ./Select 0.0.0.0 1688" << std::endl;return -1;}int sockFd = EchoServer::CreateListenSocket(argv[1], atoi(argv[2]), false);if (sockFd < 0) {return -1;}int maxFd;fd_set readSet;EchoServer::SetNotBlock(sockFd);std::unordered_set<int> clientFds;while (true) {updateReadSet(clientFds, maxFd, sockFd, readSet);int ret = select(maxFd + 1, &readSet, NULL, NULL, NULL);if (ret <= 0) {if (ret < 0) perror("select failed");continue;}for (int i = 0; i <= maxFd; i++) {if (not FD_ISSET(i, &readSet)) {continue;}if (i == sockFd) { // 监听的sockFd可读,则表示有新的链接EchoServer::LoopAccept(sockFd, 1024, [&clientFds](int clientFd) {clientFds.insert(clientFd); // 新增到要监听的fd集合中});continue;}handlerClient(i);clientFds.erase(i);close(i);}}return 0;
}
poll
在 select() 中文件描述符集合是由一个大小为1024的位图实现的,为了支持监听更多的文件描述符,poll 结构体数组存放描述符集合。
struct pollfd {int fd; // 文件描述符short events; // 监听的事件short revents; // 返回的事件
}
函数接口
#include <poll.h>
// nfds 指明fds数组的大小
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
示例
void updateFds(std::unordered_set<int> &clientFds, pollfd **fds, int &nfds) {if (*fds != nullptr) {delete[](*fds);}nfds = clientFds.size();*fds = new pollfd[nfds];int index = 0;for (const auto &clientFd : clientFds) {(*fds)[index].fd = clientFd;(*fds)[index].events = POLLIN;(*fds)[index].revents = 0;index++;}
}void handlerClient(int clientFd) {std::string msg;if (not EchoServer::RecvMsg(clientFd, msg)) {return;}EchoServer::SendMsg(clientFd, msg);
}int main(int argc, char *argv[]) {if (argc != 3) {std::cout << "invalid input" << std::endl;std::cout << "example: ./Poll 0.0.0.0 1688" << std::endl;return -1;}int sockFd = EchoServer::CreateListenSocket(argv[1], atoi(argv[2]), false);if (sockFd < 0) {return -1;}int nfds = 0;pollfd *fds = nullptr;std::unordered_set<int> clientFds;clientFds.insert(sockFd);EchoServer::SetNotBlock(sockFd);while (true) {updateFds(clientFds, &fds, nfds);int ret = poll(fds, nfds, -1);if (ret <= 0) {if (ret < 0) perror("poll failed");continue;}for (int i = 0; i < nfds; i++) {if (not(fds[i].revents & POLLIN)) {continue;}int curFd = fds[i].fd;if (curFd == sockFd) {EchoServer::LoopAccept(sockFd, 1024, [&clientFds](int clientFd) {clientFds.insert(clientFd); // 新增到要监听的fd集合中});continue;}handlerClient(curFd);clientFds.erase(curFd);close(curFd);}}return 0;
}
poll 和 select 的区别
- 数据结构和参数
- select: 使用固定大小的位图(fd_set)来表示文件描述符集合,上限为1024.
- poll: 使用动态数组(pollfd 结构体数组)来表示文件描述符集合。
- 性能
- select: 每次调用时,用户态的文件描述符集合需要复制到内核态。
- poll: 通过传递指针的方式避免了每次调用时复制整个文件描述符集合到内核。
epoll
原理:
-
事件注册:使用epoll_create创建一个epoll文件描述符,然后通过epoll_ctl向这个文件描述符注册事件(如读、写、错误等)和对应的socket描述符。
-
事件等待:通过epoll_wait函数等待一个或多个事件的发生。这个调用是阻塞的,直到至少有一个已注册的事件发生,或者超时,或者被中断。相比于select和poll,epoll_wait的优势在于它不会随着监听的文件描述符数量增加而导致效率下降,因为它内部维护了一个高效的红黑树结构来管理这些描述符。
-
事件处理:当epoll_wait返回时,会给出一个就绪事件的列表,应用可以直接对这些事件进行处理,而无需遍历所有监控的文件描述符。
水平触发和边缘触发:
- LT模式下,只要事件未被处理,每次调用epoll_wait都会返回该事件。
- ET模式下,事件仅在状态发生变化的那一刻返回一次,要求应用程序一次性处理完所有就绪的数据,否则可能会丢失事件。
示例
void handlerClient(int clientFd) {std::string msg;if (not EchoServer::RecvMsg(clientFd, msg)) {return;}EchoServer::SendMsg(clientFd, msg);
}int main(int argc, char *argv[]) {if (argc != 3) {std::cout << "invalid input" << std::endl;std::cout << "example: ./Epoll 0.0.0.0 1688" << std::endl;return -1;}int sockFd = EchoServer::CreateListenSocket(argv[1], atoi(argv[2]), false);if (sockFd < 0) {return -1;}epoll_event events[2048];int epollFd = epoll_create(1024);if (epollFd < 0) {perror("epoll_create failed");return -1;}EchoServer::Conn conn(sockFd, epollFd, false);EchoServer::SetNotBlock(sockFd);EchoServer::AddReadEvent(&conn);while (true) {int num = epoll_wait(epollFd, events, 2048, -1);if (num < 0) {perror("epoll_wait failed");continue;}for (int i = 0; i < num; i++) {EchoServer::Conn *conn = (EchoServer::Conn *)events[i].data.ptr;if (conn->Fd() == sockFd) {EchoServer::LoopAccept(sockFd, 2048, [epollFd](int clientFd) {EchoServer::Conn *conn = new EchoServer::Conn(clientFd, epollFd, false);EchoServer::AddReadEvent(conn); // 监听可读事件,保持fd为阻塞IOEchoServer::SetTimeOut(conn->Fd(), 0, 500000); // 设置读写超时时间为500ms});continue;}handlerClient(conn->Fd());EchoServer::ClearEvent(conn);delete conn;}}return 0;
}
异步I/O
相关文章:
网络I/O模型
网络I/O模型 同步I/O阻塞I/O非阻塞I/OI/O多路复用select函数接口示例 poll函数接口示例 poll 和 select 的区别epoll原理:示例 异步I/O 同步I/O 阻塞I/O 一个基本的C/S模型如下图所图:其中 listen()、connect()、write()、read() 都是阻塞I/O࿰…...
Docker 简介和安装
目录 Docker 是什么 跟普通虚拟机的对比 打包、分发、部署 Docker 部署的优势 Docker 通常用来做什么 重要概念:镜像、容器 安装 镜像加速源 Docker 是什么 Docker 是一个应用打包、分发、部署的工具 你也可以把它理解为一个轻量的虚拟机,它只虚…...
【源码】Spring Data JPA原理解析之Repository自定义方法命名规则执行原理(二)
Spring Data JPA系列 1、SpringBoot集成JPA及基本使用 2、Spring Data JPA Criteria查询、部分字段查询 3、Spring Data JPA数据批量插入、批量更新真的用对了吗 4、Spring Data JPA的一对一、LazyInitializationException异常、一对多、多对多操作 5、Spring Data JPA自定…...
Vue前端中从后端获取图片验证码
前端发送请求 <template><el-form :model"user" :rules"rules" ref"userForm" class"login" label-width"auto" style"max-width: 600px"><el-form-item label"用户名" prop"name…...
【源码】多语言H5聊天室/thinkphp多国语言即时通讯/H5聊天室源码/在线聊天/全开源
多语言聊天室系统,可当即时通讯用,系统默认无需注册即可进入群聊天,全开源 【海外聊天室】多语言H5聊天室/thinkphp多国语言即时通讯/H5聊天室源码/在线聊天/全开源 - 吾爱资源网...
gitlab 创建 ssh 和 token
文章目录 一、创建ssh key二、将密钥内容复制到gitlab三、创建token 一、创建ssh key 打开控制台cmd,执行命令 ssh-keygen -t rsa -C xxxxx xxxxx是你自己的邮箱 C:\Users\xx\.ssh 目录下会创建一个名为id_rsa.pub的文件,用记事本打开,并…...
Docker - Kafka
博文目录 文章目录 说明命令 说明 Docker Hub - bitnami/kafka Docker Hub - apache/kafka Kafka QuickStart Kafka 目前没有 Docker 官方镜像, 目前拉取次数最多的是 bitnami/kafka, Apache 提供的是 apache/kafka (更新最及时), 本文使用 bitnami/kafka bitnami/kafka 镜像…...
一键实现文件夹批量高效重命名:轻松运用随机一个字母命名,让文件管理焕然一新!
在数字化时代,文件夹管理是我们日常生活和工作中不可或缺的一部分。然而,随着文件数量的不断增加,文件夹命名的繁琐和重复成为了一个让人头疼的问题。你是否曾因为手动一个个重命名文件夹而感到枯燥乏味?你是否曾渴望有一种方法能…...
Vue3项目练习详细步骤(第二部分:主页面搭建)
主页面搭建 页面主体结构 路由 子路由 主页面搭建 页面主体结构 在vuews目录下新建Layout.vue文件 主页面内容主体代码 <script setup> import {Management,Promotion,UserFilled,User,Crop,EditPen,SwitchButton,CaretBottom } from element-plus/icons-vue imp…...
[个人总结]-java常用方法
1.获取项目根路径 user.dir是一个系统属性,表示用户当前的工作目录,大多数情况下,用户的当前工作目录就是java项目的根目录(src文件的同级路径) System.getProperty("user.dir") 结果:D:\code…...
什么是Java泛型?它有什么作用
Java泛型(Generics)是一种允许在定义类、接口和方法时使用类型参数的机制。泛型提供了一种机制,使得代码可以对多种类型的对象进行操作,而无需进行类型转换。 Java泛型的作用 类型安全:通过在编译时进行类型检查&…...
[机缘参悟-197] - 《道家-水木然人间清醒1》读书笔记 -21-看问题从现象到本质的层次
目录 1. 现象层: 2. 关联层: 3. 原因层: 4. 本质层: 5. 解决方案层: 6. 设计实现层: 7. 泛化: 8. 创新与发现: 看问题从现象到本质的层次是一个逐步深入、由表及里的过程。这…...
AIGC商业案例实操课,发觉其创造和商业的无限可能,Ai技术在行业应用新的商机
课程下载:https://download.csdn.net/download/m0_66047725/89307523 更多资源下载:关注我。 课程内容 1 AI为什么火 。写在课程前面的寄语 。AIGC标志性事件:太空歌剧院 。AI人工智能为什么这么火 ,AI人工智能发展历程 。聊天AI会取…...
Java学习路径图
1.学习路径 JAVA架构师学习路径 2.路径拆解 2.1 Spring 2.1.1 SpringBoot原理 SpringBoot2学习视频 SpringBoot2笔记 SpringBoo2代码 2.2.2 SpringBoot项目 《谷粒商城》学习视频...
文章解读与仿真程序复现思路——电力系统自动化EI\CSCD\北大核心《考虑动态定价的新能源汽车能源站优化运行》
本专栏栏目提供文章与程序复现思路,具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…...
【简单讲解下Fine-tuning BERT,什么是Fine-tuning BERT?】
🎥博主:程序员不想YY啊 💫CSDN优质创作者,CSDN实力新星,CSDN博客专家 🤗点赞🎈收藏⭐再看💫养成习惯 ✨希望本文对您有所裨益,如有不足之处,欢迎在评论区提出…...
Docker搭建Redis主从 + Redis哨兵模式(一主一从俩哨兵)
我这里是搭建一主一从,俩哨兵,准备两台服务器,分别安装docker 我这里有两台centos服务器 主服务器IP:192.168.252.134 从服务器IP:192.168.252.135 1.两台服务器分别拉取redis镜像 docker pull redis 2.查看镜像 d…...
Three.js——tween动画、光线投射拾取、加载.obj/.mtl外部文件、使用相机控制器
个人简介 👀个人主页: 前端杂货铺 ⚡开源项目: rich-vue3 (基于 Vue3 TS Pinia Element Plus Spring全家桶 MySQL) 🙋♂️学习方向: 主攻前端方向,正逐渐往全干发展 …...
内网渗透-在HTTP协议层面绕过WAF
进入正题,随着安全意思增强,各企业对自己的网站也更加注重安全性。但很多web应用因为老旧,或贪图方便想以最小代价保证应用安全,就只仅仅给服务器安装waf。 本次从协议层面绕过waf实验用sql注入演示,但不限于实际应用…...
qt QGroupBox radiobutton
QGroupBox 显示文本:属性 title 加载radiobutton if (jsonObject.contains("startEndTogether") && jsonObject["startEndTogether"].isString()) {QString selectedButton jsonObject["startEndTogether"].toString();//…...
iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...
NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...
NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...
音视频——I2S 协议详解
I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...
Caliper 配置文件解析:fisco-bcos.json
config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...
第7篇:中间件全链路监控与 SQL 性能分析实践
7.1 章节导读 在构建数据库中间件的过程中,可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中,必须做到: 🔍 追踪每一条 SQL 的生命周期(从入口到数据库执行)&#…...
