C++网络编程之IO多路复用(三)

概述

        在前两篇文章中,我们介绍了如何使用select和poll进行IO多路复用。select通常有一个固定的文件描述符数量上限(通常是1024),poll虽然没有严格的文件描述符数量限制,但在实际使用中也可能受到系统资源的限制。相比之下,epoll支持非常大的文件描述符数量(理论上可以达到系统文件描述符的最大值),因此更适合高并发场景。在本篇中,我们将重点介绍epoll。

epoll

        epoll是Linux特有的IO多路复用接口,专为大规模并发场景设计。epoll相比于select和poll有更高的性能,因为它可以处理大量的文件描述符,并且在获取事件时不需要遍历整个文件描述符集合。epoll的内部实现中使用了红黑树和就绪链表两种数据结构,以便高效地管理和查询事件。

        epoll的核心思想是:创建一个事件表,这个事件表会将所有需要监控的文件描述符和它们对应的事件关联起来。当某个文件描述符上的事件发生时,内核会将该事件添加到就绪列表中。用户程序只需要从就绪列表中读取事件即可,而不需要像select或poll那样每次都需要遍历整个文件描述符集来查找哪些事件已经就绪。

        与epoll相关的系统API主要有三个:epoll_create、epoll_ctl、epoll_wait,下面分别进行介绍。

        1、epoll_create函数用于创建一个epoll实例,并返回一个新的文件描述符。在较新的Linux版本中,推荐使用epoll_create1函数。epoll_create1是epoll_create的改进版,允许指定标志位。

int epoll_create(int size);
int epoll_create1(int flags);

        size:这个参数在现代内核中已经不再使用,但在调用时仍需要传递一个大于零的值。它是历史遗留下来的,用于向后兼容。

        flags:可以是0或EPOLL_CLOEXEC。EPOLL_CLOEXEC标志表示执行exec函数族时,关闭此文件描述符。

        返回值:成功时返回新的文件描述符(非负整数),失败时返回-1,并设置errno。

        2、epoll_ctl函数用于向epoll实例添加、修改或删除感兴趣的文件描述符。

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

        epfd:由epoll_create或epoll_create1创建的epoll文件描述符。

        op:操作类型,可以是以下之一:

        (1)EPOLL_CTL_ADD:将文件描述符fd添加到epoll实例中。

        (2)EPOLL_CTL_MOD:修改文件描述符fd在epoll实例中的事件。

        (3)EPOLL_CTL_DEL:从epoll实例中移除文件描述符fd。

        fd:要操作的文件描述符。

        event:指向epoll_event结构体的指针,定义了监听的事件类型和关联的数据。

        返回值:成功时返回0,失败时返回-1,并设置errno。

        3、epoll_wait函数用于等待并获取就绪的IO事件。

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

        epfd:由epoll_create或epoll_create1创建的epoll文件描述符。

        events:指向epoll_event数组的指针,用来存放发生的事件。

        maxevents:events数组的最大长度。

        timeout:等待时间,单位为毫秒。-1表示无限等待,0表示立即返回,不阻塞。

        返回值:成功时返回发生的事件数量,失败时返回-1,并设置errno。

边缘触发模式

        epoll支持边缘触发(Edge-Triggered,即ET)模式,select和poll不支持,它们仅支持水平触发模式(Level-Triggered,即LT)。边缘触发模式是一种高效的IO事件通知机制,它与水平触发模式有所不同。在边缘触发模式下,epoll只会在文件描述符的状态发生变化时通知应用程序一次。这意味着,如果应用程序没有处理完所有数据,内核将不会再次通知该事件,直到新的数据到达或状态再次发生变化。

        epoll的边缘触发模式具有以下几个特点。

        1、仅在状态变化时通知。当文件描述符从不可读变为可读,或者从不可写变为可写时,epoll会通知应用程序。如果应用程序没有完全读取或写入所有数据,那么剩余的数据将不会再次触发事件,直到有新的数据到来或状态再次变化。

        2、非阻塞IO。在边缘触发模式下,必须使用非阻塞IO。这是因为当read或write调用返回EAGAIN或EWOULDBLOCK时,表示当前没有更多数据可以读取或写入,但并不意味着文件描述符已经不可读或不可写。应用程序需要继续尝试读取或写入,直到所有数据处理完毕。

        3、更高的性能。由于边缘触发模式减少了内核和用户空间之间的上下文切换次数,因此通常比水平触发模式更高效,特别是在高并发场景中。

        4、更复杂的编程模型。使用边缘触发模式需要更加小心地处理IO操作,以确保不会遗漏任何数据。

实战代码

        在下面的示例代码中,我们使用epoll函数实现了TCP服务器的IO多路复用。

        首先,我们创建一个监听套接字listen_sock,将其绑定到指定的端口8888,并开始监听连接请求。

        然后,我们使用epoll_create1创建一个epoll实例,并检查是否成功。接着,将监听套接字listen_sock添加到 epoll实例中,并设置为边缘触发模式(EPOLLET)和可读事件(EPOLLIN)。

        在主循环中,我们使用epoll_wait函数等待IO事件的发生。epoll_wait会阻塞,直到有事件发生或超时,返回值nfds是就绪事件的数量。遍历events数组中的每一个就绪事件,检查每个事件的文件描述符fd是否为监听套接字listen_sock。

        如果事件的文件描述符是监听套接字listen_sock,则表示有新的连接请求。调用accept接受新连接,并创建一个新的客户端套接字conn_sock。将新连接的套接字添加到epoll实例中,并设置为边缘触发模式(EPOLLET)和可读事件(EPOLLIN)。

        如果事件的文件描述符不是监听套接字listen_sock,则表示该文件描述符上有数据可读。调用read函数读取数据,并回显给客户端。如果读取到0字节,表示客户端已断开连接。如果读取失败,也认为客户端断开连接。在这两种情况下,关闭套接字,并从epoll实例中移除该套接字。

        最后,当程序退出时,关闭所有打开的套接字和epoll实例。

#include <iostream>
#include <sys/epoll.h>
#include <unistd.h>
#include <fcntl.h>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>using namespace std;#define MAX_EVENTS                  10int main()
{int listen_sock = socket(AF_INET, SOCK_STREAM, 0);if (listen_sock == -1){cout << "Create socket failed" << endl;return 1;}struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(8888);server_addr.sin_addr.s_addr = INADDR_ANY;if (bind(listen_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1){cout << "Bind failed" << endl;close(listen_sock);return 1;}if (listen(listen_sock, 5) == -1){cout << "Listen failed" << endl;close(listen_sock);return 1;}// 创建epoll实例int epoll_fd = epoll_create1(0);if (epoll_fd == -1){cout << "Create epoll failed" << endl;close(listen_sock);return 1;}// 添加监听Socketstruct epoll_event ev, events[MAX_EVENTS];ev.events = EPOLLIN | EPOLLET;ev.data.fd = listen_sock;epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_sock, &ev);while (true){// 等待并获取就绪的IO事件int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);if (nfds == -1){cout << "Wait epoll failed" << endl;break;}for (int i = 0; i < nfds; ++i){if (events[i].data.fd == listen_sock){// 新连接struct sockaddr_in client_addr;socklen_t client_len = sizeof(client_addr);int conn_sock = accept(listen_sock, (struct sockaddr*)&client_addr, &client_len);if (conn_sock != -1){ev.events = EPOLLIN | EPOLLET;ev.data.fd = conn_sock;epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_sock, &ev);}else{cout << "New connection" << endl;}}else{// 处理数据char buf[1024];ssize_t nread = read(events[i].data.fd, buf, sizeof(buf));if (nread > 0){// 接收并回显数据cout << "Received data: " << string(buf, nread) << endl;write(events[i].data.fd, buf, nread);}else if (nread == 0){// 客户端关闭连接epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, nullptr);close(events[i].data.fd);}else{// 发生错误cout << "Read error" << endl;epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, nullptr);close(events[i].data.fd);}}}}close(listen_sock);close(epoll_fd);return 0;
}

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

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

相关文章

如何让 AI 更懂你:提示词的秘密

目录 前言常见概念大语言模型&#xff08;Large Language Model&#xff09;大模型输入常见参数提示词工程的限制 战略思想效果评测复杂任务拆解提示词结构化加示例加要求加维度 提示词框架 前言 小册推荐&#xff1a;https://juejin.cn/book/7399064580883742774 评估响应内…

特征值分解原理和实战

特征值分解&#xff08;Eigenvalue Decomposition&#xff09;是线性代数中的一种重要技术&#xff0c;用于分析矩阵的内在属性。这种分解方法主要适用于方阵&#xff08;即行数和列数相等的矩阵&#xff09;&#xff0c;用于将矩阵分解为其特征向量和特征值。 基本原理 假设 …

99人!关于第十八届中国青年科技奖拟表彰对象的公示!

本期精选SCI&EI ●IEEE 1区TOP 计算机类&#xff08;含CCF&#xff09;&#xff1b; ●EI快刊&#xff1a;最快1周录用&#xff01; 知网(CNKI)、谷歌学术期刊 ●7天录用-检索&#xff08;100%录用&#xff09;&#xff0c;1周上线&#xff1b; 免费稿件评估 免费匹配…

cherno引擎课 -

感谢b站星云图形的翻译&#xff1a;【双语】【最佳游戏引擎教程实战】【入门】&#xff08;1&#xff09;&#xff1a;Introducing the GAME ENGINE series!_哔哩哔哩_bilibili Introducing the GAMEENGINE series 希望&#xff1a;它是一个制作(互动)3D实时渲染应用程序的引…

【自然资源】吉林一号卫星有关知识,你要了解

吉林一号卫星有关知识&#xff0c;你要了解 吉林一号商业卫星是中国第一套自主研发的商用遥感卫星星座&#xff0c;由长光卫星技术有限公司研制。 “吉林一号”商业卫星组星包括1颗光学遥感卫星、2颗视频卫星和1颗技术验证卫星&#xff0c;工作轨道均为高约650公里的太阳同步轨…

博图与Factory I/O结合实现运料小车自动往返四次控制

一、示例要求 按照上图所示完成运料小车的往返控制&#xff0c;由于对虚拟工厂中零件应用不熟悉&#xff0c;所以料斗门和小车底门都用M0.x代替完成&#xff1b;只对传送带有了解&#xff0c;因此此处借助传送带使得纸箱达到往返运动。 二、I/O分配表 类别元件I/O端口编号备注…

扩散模型和重新照明的未来

重新照明&#xff08;relighting&#xff09;是在给定输入场景的情况下&#xff0c;在指定的目标照明条件下渲染场景的任务。这是计算机视觉和图形学中的关键任务。然而&#xff0c;这是一个不适定问题&#xff0c;因为场景中物体的外观是由光源、几何形状和表面材料属性等因素…

想学习海量数据的管理方法?听这节课就够了

在大模型时代&#xff0c;由于模型规模的升级&#xff0c;需要处理的数据量也大幅增加&#xff0c;因此对数据管理提出了更高的要求。 首先&#xff0c;分布式存储系统对于企业来说至关重要&#xff0c;将数据分散存储在多个节点上&#xff0c;可以提高数据的访问速度和处理能…

Works With线上开发者大会将提供物联网行业深入的专业知识和技能

Silicon Labs2024年Works With线上开发者大会定于11月20日至21日举行&#xff0c;将汇集全球各地的物联网开发人员、设备制造商、无线技术专家、工程师和商业领袖&#xff0c;观众可免费注册参加。同时&#xff0c;为了方便中文观众&#xff0c;所有在线视频均配有中文字幕。 芯…

2024 年(第 7 届)“泰迪杯”数据分析技能赛B 题 特殊医学用途配方食品数据分析 完整代码 结果 可视化分享

一、背景特殊医学用途配方食品简称特医食品&#xff0c;是指为满足进食受限、消化吸收障碍、代谢素乱或者特定疾病状态人群对营养素或者膳食的特殊需要&#xff0c;专门加工配置而成的配方食品&#xff0c;包括0月龄至12月龄的特殊医学用途婴儿配方食品和适用于1岁以上的特殊医…

【日记】总感觉搞这些才回到了自己的老本行……(1179 字)

正文 前两天忘记写了几件事。 一是&#xff0c;周五晚上打开自己的博客&#xff0c;准备翻自己的笔记&#xff0c;然后…… 我发现&#xff0c;博客的子列表打不开了。我知道&#xff0c;完蛋了。Mdbook 它又双叒叕更新了。这次不知道又把 index.hbs 哪里给改了。 反正&#xf…

2024年11月11日历史上的今天大事件早读

700年11月11日宰相狄仁杰逝世 1821年11月11日《罪与罚》的作者陀思妥耶夫斯基诞辰 1844年11月11日清政府在五口通商口岸解除天主教禁 1851年11月11日御史王茂荫提出请变科举 1861年11月11日清皇太子载淳即皇帝位 1891年11月11日热河金丹教杨悦春发动反洋教起义 1918年11月…

【ESP】一小时速通入门笔记

【ESP】一小时速通入门笔记 前言: 之前上学时就用过一次esp32, 当时初次使用搭建编译环境费了老大功夫.在我第一篇esp32笔记中也有说明.以至于我好像忘记记录完整的入门笔记了.最近因为工作需要又开始接触esp32才发现,现在已经方便得多了.顺利的话一小时速通. 前排提醒: 本笔记…

海量小文件挑战下的CephFS:优化策略与实践探索

文章目录 1.背景2.基本概念2.1 CephFS IO流程2.2 Ceph-FUSE 3. 问题3.1 问题源起3.2 理论分析3.3 原因排查3.3.1 Ceph-FUSE日志分析3.3.2 提出猜想3.3.3 代码验证3.3.3.1 MDS端3.3.3.2 Ceph-FUSE端 3.4 小结 1.背景 随着大数据、人工智能技术的蓬勃发展&#xff0c;人类对于算…

团队发展与个人价值的共生

一、团队属性 团队的本质属性&#xff0c;在很大程度上表现为一种选择性和甄别性&#xff0c;即排外与排异。这意味着团队需要筛选出具有相同目标、价值观以及能力互补的成员&#xff0c;以达成高效的协作与共同进步。在团队运作的过程中&#xff0c;首要任务是明确并深入理解…

git命令及原理

git: 目录则被称之为“树” 文件被称作 Blob 对象. git help <command>: 获取 git 命令的帮助信息 git init: 创建一个新的 git 仓库&#xff0c;其数据会存放在一个名为 .git 的目录下 git status: 显示当前的仓库状态 git add <filename>: 添加文件到暂存区 git …

大模型的记忆困境:平衡持续学习与灾难性遗忘

1. 引言 持续学习是智能的关键方面。它指的是从非平稳数据流中增量学习的能力&#xff0c;对于在非平稳世界中运作的自然或人工智能体来说是一项重要技能。人类是优秀的持续学习者&#xff0c;能够在不损害先前学习技能的情况下增量学习新技能&#xff0c;并能够将新信息与先前…

实时高效,全面测评快递100API的物流查询功能

一、引言 你是否曾经在网购后焦急地等待包裹&#xff0c;频繁地手动刷新订单页面以获取最新的物流信息&#xff1f;或者作为一名开发者&#xff0c;正在为如何在自己的应用程序中高效地实现物流查询功能而发愁&#xff1f;其实&#xff0c;有一个非常好用的解决方案——快递10…

HR人才评价系统软件选择攻略

企业对人力资源管理价值的日益认同&#xff0c;人才评价系统软件市场迎来了蓬勃发展的春天。在数字化转型的浪潮中&#xff0c;企业对人才的多元化需求愈发明显&#xff0c;传统的评价手段已难以跟上时代的步伐。因此&#xff0c;人才评价系统软件应运而生&#xff0c;迅速成为…

蓝桥杯真题——good-sequence(C语言)

问题描述 一个序列 [b1,b2,...,bm] 若对于 2≤i≤m 满足 bi≤b1 &#xff0c;则称为好序列。 现在给定 [a1,a2,...,an] &#xff0c;求对于该序列的每一个后缀 [ak,ak1,...,an](1≤k≤n)最少能划分成多少个好序列。 输入格式 第一行包含一个整数 n &#xff0c;表示数组 a …