IO多路复用(select/epoll)

目录

一、概念

二、语法

1.select

1.1 select函数的语法

1.2 文件描述符集合操作

1.3 select函数的优缺点

2.epoll

2.1 epoll语法

2.2 epoll的工作模式

2.3 epoll的优缺点

三、select服务端代码

四、epoll服务端代码

五、客户端代码


 

一、概念

IO多路复用是一种同步的I/O模型,它允许一个进程同时监视多个文件描述符,一旦某个文件描述符就绪(可以进行I/O操作),就能够通知程序进行相应的读写操作。

IO多路复用有三种实现方式:select、poll、epoll。


 

二、语法

1.select

select是最早的I/O多路复用技术之一,它使用fd_set数据结构来存储和跟踪文件描述符。

select是基于线性方式处理待检测集合的,因此每次都要遍历集合;对于返回的集合,还需要判断文件描述符是否就绪。

 

1.1 select函数的语法

select ( int nfds,

            fd_set *readfds,
            fd_set *writefds,
            fd_set *exceptfds,
            struct timeval *timeout );

  • nfds:监控的文件描述符集里最大文件描述符加1。
  • readfds:文件描述符集合,内核会监视此集合中的文件是否有数据可读,传入传出参数。
  • writefds:文件描述符集合,内核会监视此集合中的文件是否有数据可写,传入传出参数。
  • exceptfds:文件描述符集合,用于监视此集合中的文件是否有异常情况,传入传出参数。
  • timeout:设置为NULL:阻塞,若检测到就绪的文件描述符则返回其数量;设置时间:等待固定时间,若没有就绪的文件描述符则返回0。

 

1.2 文件描述符集合操作

  • FD_ZERO(fd_set *set):清空文件描述符集合。
  • FD_SET(int fd, fd_set *set):将文件描述符fd添加到集合set中。
  • FD_CLR(int fd, fd_set *set):从集合set中移除文件描述符fd。
  • FD_ISSET(int fd, fd_set *set):检查文件描述符fd是否在集合set中。

 

1.3 select函数的优缺点

  • 优点:适用于多种操作系统,具有较好的兼容性。
  • 缺点:当文件描述符数量较多时,效率会比较低。此外,select有一个固定的文件描述符数量限制,通常为1024。

 

2.epoll

epoll是一种高效且可扩展的I/O多路复用技术。epoll是基于红黑树来处理待检测集合的,使用回调机制,处理效率高;其返回的集合中的文件描述符都是就绪的,无需再次进行检测。

epoll主要通过以下三个函数来实现其功能:epoll_create、epoll_ctl、epoll_wait。

 

2.1 epoll语法

①epoll_create函数

epoll_create (int size);

创建一个epoll对象,返回一个文件描述符,用于后续的操作,参数size大于0即可。

②epoll_ctl函数

epoll_ctl ( int epfd, int op, int fd,
               struct epoll_event *event );

向epoll对象中添加、修改或删除文件描述符。

  • epfd:epoll_create函数返回的epoll文件描述符。
  • op:操作类型,EPOLL_CTL_ADD(添加)、EPOLL_CTL_MOD(修改)或EPOLL_CTL_DEL(删除)。
  • fd:要监听的文件描述符。
  • event:指向epoll_event结构体的指针,用于指定要监听的事件类型。
epoll_event和epoll_data:

struct epoll_event
{
  uint32_t events;
  epoll_data_t data;
};

typedef union epoll_data
{
  void *ptr;
  int fd;
  uint32_t u32;
  uint64_t u64;
};

epoll_event结构体用于描述事件,成员events表示事件类型:

  • EPOLLIN:读事件,接收数据,检测读缓冲区,如果可读则该文件描述符已就绪。
  • EPOLLOUT:写事件,发送数据,检测写缓冲区,如果可写则该文件描述符已就绪。
  • EPOLLET:设置为边沿模式,默认是水平模式。

epoll_data为联合体,通常使用int类型的fd,用于存储发生对应事件的文件描述符。

 ③epoll_wait函数

epoll_wait ( int epfd, struct epoll_event *events,
               int maxevents, int timeout );

等待epoll对象中的事件发生,返回发生事件的文件描述符集合。

  • epfd:epoll_create函数返回的epoll文件描述符。
  • events:一个数组,用来存储就绪的事件信息。
  • maxevents:数组events的大小,即最多可以返回多少个就绪的文件描述符。
  • timeout:表示epoll_wait阻塞等待的时间(毫秒)。若为负数则会阻塞,直到有事件就绪;若为0则不阻塞,立即返回当前已就绪的事件。

 

2.2 epoll的工作模式

①水平触发(LT)模式:

水平触发是epoll的默认工作模式。在这种模式下,当文件描述符上的事件就绪时,epoll_wait函数会返回,并且如果该事件没有被处理,epoll_wait函数会在下一次调用时再次返回,直到该事件被处理。例如,当一个套接字上有数据可读时,epoll_wait会返回,并且如果数据没有被读取,epoll_wait会在下一次调用时再次返回,直到数据被读取。

②边沿触发(ET)模式:

边沿触发模式下,epoll_wait函数只会在文件描述符的状态发生变化时返回。例如,当一个套接字上有数据可读时,epoll_wait会返回,但是如果数据没有被读取,epoll_wait不会在下一次调用时再次返回,直到有新的数据到达。因此在边沿触发模式下,程序需要在一次epoll_wait调用返回后,立即处理所有就绪的事件。

③总结:
  • 边沿触发模式通常比水平触发模式的性能更高,因为它调用epoll_wait函数的次数比较少。
  • 水平触发模式更适合处理阻塞式I/O,而边沿触发模式更适合处理非阻塞式I/O。

epoll在边沿模式下,必须将套接字设置为非阻塞模式,此时需要循环读取读缓冲区的数据,读取完后recv函数会返回-1,此时需要特殊处理。

 

2.3 epoll的优缺点

  • 优点:高效的事件驱动模型,支持大量并发连接,没有固定的文件描述符数量限制。
  • 缺点:仅适用于Linux系统,不具备跨平台性。

 

三、select服务端代码

#include<iostream>
#include<cstring>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>using namespace std;int main(int argc, char* argv[])
{int lfd = socket(AF_INET, SOCK_STREAM, 0);if (lfd == -1) {cerr << "error" << endl;return -1;}struct sockaddr_in local;local.sin_family = AF_INET;local.sin_port = htons(8080);local.sin_addr.s_addr = INADDR_ANY;if (bind(lfd, (struct sockaddr*)&local, sizeof(local)) == -1) {cerr << "error" << endl;return -1;}if (listen(lfd, 128) == -1) {cerr << "error" << endl;return -1;}fd_set readset;FD_ZERO(&readset);FD_SET(lfd, &readset);int maxfd = lfd;while (true) {fd_set tmp = readset;int ret = select(maxfd + 1, &tmp, NULL, NULL, NULL);if (FD_ISSET(lfd, &tmp)) {int cfd = accept(lfd, NULL, NULL);FD_SET(cfd, &readset);maxfd = max(maxfd, cfd);cout << "connect: " << cfd << endl;}for (int i = 0; i <= maxfd; i++) {if (i != lfd && FD_ISSET(i, &tmp)) {char buffer[1024] = { 0 };int len = recv(i, buffer, sizeof buffer, 0);if (len == -1) {cerr << "error" << endl;return -1;}else if (len == 0) {cout << "client disconnect: " << i << endl;FD_CLR(i, &readset);close(i);break;}cout << buffer << endl;for (int i = 0; i < len; i++) {buffer[i] = toupper(buffer[i]);}len = send(i, buffer, strlen(buffer) + 1, 0);if (len == -1) {cerr << "error" << endl;return -1;}}}}close(lfd);return 0;
}

 

四、epoll服务端代码

#include<iostream>
#include<cstring>
#include<sys/socket.h>
#include<netinet/in.h>
#include<sys/epoll.h>
#include<unistd.h>
#include<fcntl.h>
#include<errno.h>using namespace std;int main(int argc, char* argv[])
{int lfd = socket(AF_INET, SOCK_STREAM, 0);if (lfd == -1) {cerr << "error" << endl;return -1;}struct sockaddr_in local;local.sin_family = AF_INET;local.sin_port = htons(8080);local.sin_addr.s_addr = INADDR_ANY;if (bind(lfd, (struct sockaddr*)&local, sizeof(local)) == -1) {cerr << "error" << endl;return -1;}if (listen(lfd, 128) == -1) {cerr << "error" << endl;return -1;}int epfd = epoll_create(1);struct epoll_event event;event.events = EPOLLIN;event.data.fd = lfd;epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &event);struct epoll_event events[1024];while (true) {int fds = epoll_wait(epfd, events, 1024, -1);for (int i = 0; i < fds; i++) {int fd = events[i].data.fd;if (fd == lfd) {int cfd = accept(lfd, NULL, NULL);struct epoll_event client_event;int flag = fcntl(cfd, F_GETFL);flag |= O_NONBLOCK;fcntl(cfd, F_SETFL, flag);client_event.events = EPOLLIN | EPOLLET;client_event.data.fd = cfd;epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &client_event);cout << "connect: " << cfd << endl;}else {while(true) {char buffer[128] = { 0 };int len = recv(fd, buffer, sizeof buffer, 0);if (len == 0) {cout << "client disconnect: " << fd << endl;epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);close(fd);break;}else if (len > 0) {cout << buffer << endl;for (int i = 0; i < len; i++) {buffer[i] = toupper(buffer[i]);}len = send(fd, buffer, strlen(buffer) + 1, 0);if (len == -1) {cerr << "error" << endl;return -1;}}else if (len == -1) {if (errno = EAGAIN) {cout << "Data read complete." << endl;break;}else {cerr << "error" << endl;return -1;}}}}}}close(lfd);return 0;
}

 

五、客户端代码

#include<iostream>
#include<cstring>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>using namespace std;int main(void)
{int client_socket = socket(AF_INET, SOCK_STREAM, 0);if (client_socket == -1) {cerr << "error" << endl;return -1;}struct sockaddr_in target;target.sin_family = AF_INET;target.sin_port = htons(8080);inet_pton(AF_INET, "127.0.0.1", &target.sin_addr.s_addr);if (connect(client_socket, (struct sockaddr*)&target, sizeof target) == -1) {cerr << "error" << endl;close(client_socket);return -1;}while (true) {char buffer1[1024] = { 0 };cout << "enter: ";cin >> buffer1;send(client_socket, buffer1, strlen(buffer1), 0);char buffer2[1024] = { 0 };int ret = recv(client_socket, buffer2, sizeof buffer2, 0);if (ret <= 0) {cout << "server disconnect." << endl;}cout << buffer2 << endl;}close(client_socket);return 0;
}

 

参考内容:
爱编程的大丙

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

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

相关文章

android stdudio环境: gradle一直安装失败

一、一直显示如下错误 The specified Gradle distribution file:/home/wangqingyuan/.gradle/wrapper/dists/gradle-8.6-bin/gradle-8.6-bin.zip does not exist. 经分析&#xff0c;是因为应用本身设置了gradle版本的地址为本地&#xff1a; 应用目录&#xff1a;gradle/gra…

解决PS 撤销卡顿

1. 关闭Windows Ink - 打开触控笔设置 - 禁用Windows Ink功能 2. 创建 PSUserConfig.txt&#xff08;注意Win10/11 可能隐藏文件扩展名&#xff09; - 位置&#xff1a;C:\Users\[用户名]\AppData\Roaming\Adobe\Adobe Photoshop CC 2019\Adobe Photoshop CC 2019 Se…

spring默认线程池SimpleAsyncTaskExecutor特点为什么要尽量避免使用

在 Spring Boot 中&#xff0c;默认的线程池配置由 TaskExecutionAutoConfiguration 类提供&#xff0c;使用的是 SimpleAsyncTaskExecutor。 SimpleAsyncTaskExecutor特点 每次调用创建新线程&#xff1a; SimpleAsyncTaskExecutor 每次执行任务时都会创建一个新线程&#xf…

软件测试 Linux 服务器监控命令的基本知识

Linux 服务器因其高效、稳定、开源等优势&#xff0c;广泛用于网络服务、数据库管理、应用开发等领域。而为了确保服务器的正常运行和性能&#xff0c;我们必须不断监控服务器的状态。这就需要我们熟悉一些基本的监控命令。 本文将详细介绍多种监控命令的使用方法及其应用。同…

Spring 的不同事务传播行为

目录 Spring 的不同事务传播行为 PROPAGATION_REQUIRES_NEW事务传播行为什么情况下会使用? 一、PROPAGATION_REQUIRES_NEW的含义 二、使用场景 三、注意事项 PROPAGATION_NESTED事务传播行为什么情况下会使用? 一、PROPAGATION_NESTED的含义 二、使用场景 三、嵌套事…

【Linux】进度条

本文中&#xff0c;我们来写一个进度条。 本文大纲&#xff1a; 写一个命令行版的进度条。 1.回车换行 2.缓冲区问题&#xff08;本文不深究&#xff09; ​ 2.1测试代码 3.写一个什么样的进度条&#xff1f; ​ version1 ​ version2 回车换行 这俩不是一个概念&…

SLAM/数字图象处理基础

概念 视差&#xff1a;相同特征的不同深度估计的偏差 BoW&#xff0c;DBoW&#xff0c;DBoW2的区别是什么 Bag of Words (BoW)、DBoW&#xff08;Dynamic Bag of Words&#xff09;和DBoW2是用于图像处理和计算机视觉中的不同特征表示和匹配方法。它们之间的主要区别如下&am…

UE5材质节点SimpleGrassWind

SimpleGrassWind节点可以模拟树叶扰动&#xff0c;或小草晃动效果 用来做风格化树、风格化草效果很好 主要节点 前三个节点分别用来控制&#xff0c;风强度&#xff0c;风重力&#xff0c;风速度&#xff0c;WPO是世界位置偏移

WeNet:面向生产的流式和非流式端到端语音识别工具包

这篇文章介绍了WeNet&#xff0c;一个面向生产的开源端到端&#xff08;E2E&#xff09;语音识别工具包。WeNet的主要特点和贡献如下&#xff1a; 统一流式和非流式识别&#xff1a;提出了一种名为U2的两阶段框架&#xff0c;能够在单一模型中同时支持流式和非流式语音识别&…

Ubuntu20.04安装Foxit Reader 福昕阅读器

Ubuntu20.04安装Foxit Reader 福昕阅读器 文章目录 Ubuntu20.04安装Foxit Reader 福昕阅读器 先更新一下源 sudo apt update sudo apt upgrade下载Foxit Reader的稳定版本 wget https://cdn01.foxitsoftware.com/pub/foxit/reader/desktop/linux/2.x/2.4/en_us/FoxitReader.e…

2024年底关于期货的工作总结

十几年程序猿出身&#xff0c;因几年前的懵懂无畏闯入期货市场&#xff0c;盈了&#xff0c;感觉期货太简单&#xff0c;飘然裸辞&#xff0c;想当财务自由者&#xff0c;全职做交易。当深入学习时&#xff0c;却亏了&#xff0c;原来市场是让人敬畏的&#xff0c;也是反人性的…

c++入门——c++输入cin和输出cout的简单使用

c输入cin、输出cout 1 cin2 cout3 cin和cout说明 c在c语言的输入、输出函数的基础上进行了封装。 1 cin c可以理解为控制台&#xff0c;in可以理解为输入。 参考代码&#xff1a; void f(){int a;float b;double c;char d;cin>>a>>b>>c>>d;//这里和…

PlantUML 时序图 基本例子

基本的例子 序列-> 用于绘制两个参与者之间的信息。参与者不必明确声明。 要有一个点状的箭头&#xff0c;就用--> 也可以用<- 和<-- 。这不会改变绘图&#xff0c;但可能提高可读性。注意&#xff0c;这只适用于顺序图&#xff0c;其他图的规则不同。 plantum…

YOLOv10-1.1部分代码阅读笔记-utils.py

utils.py ultralytics\nn\modules\utils.py 目录 utils.py 1.所需的库和模块 2.def _get_clones(module, n): 3.def bias_init_with_prob(prior_prob0.01): 4.def linear_init(module): 5.def inverse_sigmoid(x, eps1e-5): 6.def multi_scale_deformable_attn_py…

vue3使用video-player实现视频播放(可拖动视频窗口、调整大小)

1.安装video-player npm install video.js videojs-player/vue --save在main.js中配置全局引入 // 导入视频播放组件 import VueVideoPlayer from videojs-player/vue import video.js/dist/video-js.cssconst app createApp(App) // 视频播放组件 app.use(VueVideoPlayer)2…

基于卷积神经网络的甲状腺结节识别系统,resnet50,mobilenet模型【pytorch框架+python源码】

更多目标检测、图像分类识别、目标追踪等项目可看我主页其他文章 功能演示&#xff1a; 甲状腺结节识别系统&#xff0c;卷积神经网络&#xff0c;resnet50&#xff0c;mobilenet【pytorch框架&#xff0c;python源码】_哔哩哔哩_bilibili &#xff08;一&#xff09;简介 …

C++--类与对象

1.封装 封装是c面向对象的三大特性之一 将属性和行为作为一个整体 将属性和行为加以权限控制 语法&#xff1a; class 类名{ 访问权限: 属性/行为 }; 访问权限 public 公共权限 类内类外均可以访问 protected 保护权限 类内可以访问&#xff0c;类外不可以访问 pr…

区块链期末复习3:跨链原子交换其他加密货币

参考教材&#xff1a;《区块链&#xff1a;技术驱动金融》 一、跨链原子交换&#xff08;不可分割的交叉链互换&#xff09; 1.实施步骤 假设Alice要拿1BTC交换Bob的3BCY。Alice作为交易的发起者。 1&#xff09;Alice创建一个secret&#xff08;一个随机数x), 并计算其哈希…

OCR实践-Table-Transformer

前言 书接上文 OCR实践—PaddleOCR Table-Transformer 与 PubTables-1M table-transformer&#xff0c;来自微软&#xff0c;基于Detr&#xff0c;在PubTables1M 数据集上进行训练&#xff0c;模型是在提出数据集同时的工作&#xff0c; paper PubTables-1M: Towards comp…

重装操作系统后 Oracle 11g 数据库数据还原

场景描述&#xff1a; 由于SSD系统盘损坏&#xff0c;更换硬盘后重装了操作系统&#xff0c;Oracle数据库之前安装在D盘(另一个硬盘)&#xff0c;更换硬盘多添加一个盘符重装系统后盘符从D变成E&#xff0c;也就是之前的D:/app/... 变成了现在的 E:/app/...&#xff0c;重新安装…