【长文梳理webserver核心】核心类篇
前言
有三个核心组件支撑一个reactor实现 [持续] 的 [监听] 一组fd,并根据每个fd上发生的事件 [调用] 相应的处理函数。这三个组件就是 EventLoop 、Channel 以及 Poller 三个类,其中 EventLoop 可以看作是对业务线程的封装,而 Channel 可以看作是对 每个已经建立连接 的封装(即accept(3) 返回的文件描述符)
EventLoop类
先明确一点,作为一个网络服务器,需要有 持续监听、持续获取监听结果、持续处理监听结果对应事件的能力,也就是 循环 去 调用Poller::poll方法获取实际发生事件的Channel集合,然后调用这些Channel里面保管的不同类型事件的处理函数,也就是Channel::HandlerEvent方法。绿色部分不懂没关系,继续看。
总之EventLoop就是负责实现 循环 ,负责驱动循环的主要模块,Channel和Poller都是他的手下,因为EventLoop整合封装了两者,并且向上提供了更方便的接口来使用。
在EventLoop的类定义中,除了⼀些状态量以外,每个 EventLoop 持有⼀个 Poller 的智能指针(对 epoll / poll 的封装),⼀个用于 EventLoop 之间通信的 Channel ,自己的线程 id,互斥锁以及装有等待处理函数的 vector 。很明显, EventLoop 并不直接管理各个连接的 Channel (文件描述符的封装),而是通过Poller 来进行的。 EventLoop 中最核心的函数就是 EventLoop::Loop() 。
EventLoop::loop(),代码如下:
void EventLoop::loop()
{ // 开始事件循环 调⽤该函数的线程必须是该EventLoop所在线程 assert(!is_looping_);assert(is_in_loop_thread());is_looping_ = true;is_stop_ = false;while(!is_stop_){// 1、epoll_wait阻塞 等待就绪事件auto ready_channels = poller_->Poll();is_event_handling_ = true;// 2、处理每个就绪事件(不同channel绑定了不同的callback)for (auto& channel : ready_channels) {channel->HandleEvents();}is_event_handling_ = false;// 3、执⾏正在等待的函数(fd注册到epoll内核事件表)PerformPendingFunctions();// 4、处理超时事件 到期了就从定时器⼩根堆中删除(定时器析构会EpollDel掉fd)poller_->HandleExpire();}is_looping_ = false;
}
每个EventLoop对象都唯一绑定了一个线程,这个线程其实就在一直执行这个函数里面的while循环,这个while循环的大致逻辑比较简单。就是调用Poller::poll方法获取事件监听器上的监听结果。接下来在loop里面就会调用监听结果中每一个Channel的处理函数HandlerEvent( )。每一个Channel的处理函数会根据Channel类中封装的实际发生的事件,执行Channel类中封装的各事件处理函数。比如一个Channel发生了可读事件,可写事件,则这个Channel的HandlerEvent( )就会调用提前注册在这个Channel的可读事件和可写事件处理函数,又比如另一个Channel只发生了可读事件,那么HandlerEvent( )就只会调用提前注册在这个Channel中的可读事件处理函数。
所以总结,每个EventLoop实际上就做了四件事
- epoll_wait阻塞 等待就绪事件(没有注册其他fd时,可以通过event_fd来异步唤醒)
- 处理每个就绪事件
- 执行正在等待的函数(fd注册到epoll内核事件表)
- 处理超时事件,到期了就从定时器小根堆中删除
Channel类
接下来解释EventLoop的两个手下之一Channel,Channel类其实相当于一个文件描述符的保姆。
想要通过 IO 多路复用(epoll / poll)监听某个文件描述符,就需要把这个 fd 和该 fd 感兴趣 的事件通过 epoll_ctl 注册到 IO 多路复用模块(事件监听器)上。当 IO 多路复用模块监听到该 fd 发生了某个事件。事件监听器返回发生事件的 fd 集合(有哪些 fd 发生了事件)以及每个 fd 的事件集合(每个 fd 具体发生了什么事件)。
Channel类则封装了一个 [fd] 和这个 [fd感兴趣事件] 以及事件监听器监听到 [该fd实际发生的事件]。同时Channel类还提供了设置该fd的感兴趣事件,以及将该fd及其感兴趣事件注册到事件监听器或从事件监听器上移除,以及保存了该fd的每种事件对应的处理函数。
Channel类重要的成员变量
- fd_:Channel对象照看的文件描述符
- events_:代表fd感兴趣的事件类型集合,或者说正在监听的事件
- revents_:代表事件监听器实际监听到该fd发生的事件类型集合,或者说是返回的就绪事件,当事件监听器监听到一个fd发生了什么事件,通过Channel::set_revents()函数来设置revents值
- last_events_:上一此事件(主要用于记录如果本次事件和上次事件⼀样 就没必要调用
- read_handler_,write_handler_,update_handler_,error_handler_:这些是std::function类型的各种回调函数,代表这个Channel为这个文件描述符保存的各事件类型发生时的处理函数。
Channel类重要的成员方法
Channel::HandleEvents()方法
/ IO事件的回调函数 EventLoop中调⽤Loop开始事件循环 会调⽤Poll得到就绪事件
// 然后依次调⽤此函数处理就绪事件void Channel::HandleEvents() {events_ = 0;// 触发挂起事件 并且没触发可读事件if ((revents_ & EPOLLHUP) && !(revents_ & EPOLLIN)) {events_ = 0;return;}// 触发错误事件if (revents_ & EPOLLERR) {HandleError();events_ = 0;return;} // 触发可读事件 | ⾼优先级可读 | 对端(客户端)关闭连接if (revents_ & (EPOLLIN | EPOLLPRI | EPOLLRDHUP)) {HandleRead();}// 触发可写事件if (revents_ & EPOLLOUT) {HandleWrite();}//处理更新监听事件(EpollMod)HandleUpdate();
}
每个Channel对象只属于一个EventLoop ,即只属于一个IO线程,只负责一个文件描述符fd的IO时间分发,但并不拥有这个fd,Channel把不同的IO事件分发为不同的回调,回调用C++11的特性function表示。
从Channel类的定义中可以看出,每个Channel持有一个文件描述符,正在监听的事件,已经发生的事件(由Poller返回),以及各个事件的回调函数的Function对象。
总的来说,Channel就是对fd事件的封装,包括注册它的事件以及回调。EventLoop通过调用Channel::handleEvent()来执行Channel的读写事件。Channel::handleEvent() 的实现也非常简单,就是比较已经发生的事件(由 Poller 返回),来调用对应的回调函数(读、写、错误)。
Poller类
Poller 类的作用就是负责监听文件描述符事件是否触发以及返回发生事件的文件描述符以及具体事件。所以一个Poller 对象对应⼀个 IO 多路复用模块。在 muduo 中,⼀个 EventLoop 对应一个Poller 。
Epoll代码如下 :
class Epoll {
public:Epoll();~Epoll();void epoll_add(const sp_Channel& request);void epoll_mod(const sp_Channel& request);void epoll_del(const sp_Channel& request);void poll(std::vector<sp_Channel>& req);
private:int epollFd_;std::vector<epoll_event> events_; // epoll_wait()返回的活动事件都放在这个数组⾥std::unordered_map<int, sp_Channel> channelMap_;
};
Poller类的主要成员变量有三个:
- epollFd_:就是用epoll_create方法返回的epoll句柄
- events:存放epoll_wait()返回的活动事件,是一个结构体
- channelMap_ :这个变量是 std::unordered_map<int, std::shared_ptr<Channel>> 类型,负责记录 文件描述符fd -> Channel 的映射,也帮忙保管所有注册在你这个 Poller 上的 Channel 。
其他函数无非就是对Epoll_ctl(4)和 Epoll_wait(4)的封装
void Epoll::poll(std::vector<sp_Channel>& req) {int event_count = Epoll_wait(epollFd_, &*events_.begin(), events_.size(), EPOLLWAIT_TIME);for(int i = 0; i < event_count; ++i) {int fd = events_[i].data.fd;sp_Channel temp = channelMap_[fd];temp->setRevents(events_[i].events);req.emplace_back(std::move(temp));}// LOG << "Epoll finished";
}
Epoll::poll(1) 这个函数可以说是 Poller 的核心了,当外部调用 poll 方法的时候,该方法底层其实是通过epoll_wait 获取这个事件监听器上发生事件的 fd 及其对应发生的事件,我们知道每个 fd 都是由⼀个Channel封 装的,通过哈希表 channelMap_ 可以根据 fd 找到封装这个 fd 的 Channel 。将 IO 多路复用模块监听到该 fd 发生 的事件写进这个 Channel 中的 revents 成员变量中。然后把这个 Channel 装进 req 中。这样,当外界调用完poll 之后就能拿到 IO 多路复用模块的监听结果( std::vector<sp_Channel>& req )。
相关文章:

【长文梳理webserver核心】核心类篇
前言 有三个核心组件支撑一个reactor实现 [持续] 的 [监听] 一组fd,并根据每个fd上发生的事件 [调用] 相应的处理函数。这三个组件就是 EventLoop 、Channel 以及 Poller 三个类,其中 EventLoop 可以看作是对业务线程的封装,而 Channel 可以看…...

[实用工具]Docker安装nextcloud实现私有云服务和onlyoffice
Nextcloud是一款开源的云存储和协作平台,允许用户在自己的服务器上存储和访问文件,同时提供强大的协作工具。它可以替代商业云存储服务,让用户拥有完全控制和自主管理自己的数据。 Nextcloud支持文件上传和下载,可以通过Web界面、…...
基于STM32设计的生猪健康检测管理系统(NBIOT+OneNet)(240)
文章目录 一、前言1.1 项目介绍【1】项目开发背景【2】设计实现的功能【3】项目硬件模块组成1.2 设计思路1.3 项目开发背景【1】选题的意义【2】可行性分析【3】参考文献【4】项目背景【5】摘要1.4 开发工具的选择【1】设备端开发【2】上位机开发1.5 系统功能总结1.6 系统框架图…...

springboot kafka多数据源,通过配置动态加载发送者和消费者
前言 最近做项目,需要支持kafka多数据源,实际上我们也可以通过代码固定写死多套kafka集群逻辑,但是如果需要不修改代码扩展呢,因为kafka本身不处理额外逻辑,只是起到削峰,和数据的传递,那么就需…...

【华为】基于华为交换机的VLAN配置与不同VLAN间通信实现
划分VLAN(虚拟局域网)主要作用: 一、提高网络安全性 广播域隔离访问控制增强 二、优化网络性能 减少网络拥塞提高网络可管理性 sysytem-view #进入系统视图配置参数 vlan batch 10 20 #批量创建vlan LSW3: int g0/0/1 port…...

力扣题11~20
题11(中等): 思路: 这种题目第一眼就是双循环,但是肯定不行滴,o(n^2)这种肯定超时,很难接受。 所以要另辟蹊径,我们先用俩指针(标志位)在最左端和最右端&am…...

更美观的HTTP性能监测工具:httpstat
reorx/httpstat是一个旨在提供更美观和详细HTTP请求统计信息的cURL命令行工具,它能够帮助开发者和运维人员深入理解HTTP请求的性能和状态。 1. 基本概述 项目地址:https://github.com/reorx/httpstat语言:该工具主要是以Python编写ÿ…...

在2024 VDC,听一曲“蓝心智能”的江河协奏
作为科技从业者,我们每年参加的终端产品发布会和开发者大会,少则几十场。说每一场都别有新意,那自然是不可能的,但每次去vivo的活动现场,总能给我耳目一新的感觉。 雨果说过,音乐可以表达难以用语言描述&am…...
Python编写的数字光刻仿真程序,使用了Hopkins光刻模型和粒子群优化(PSO)算法来优化掩模设计
Python编写的数字光刻仿真程序,使用了Hopkins光刻模型和粒子群优化(PSO)算法来优化掩模设计,以减少光刻过程中的图形偏差。 4. 定义了几个函数来模拟光波通过光刻系统的变化: - `transfer_function`:计算光波的相位变化。 - `light_source_function`:描述光源在各…...
【AD那些事 11】绘制PCB板时“隔离” 的那些事(笔记摘抄)
在设计新板子时发现需要考虑隔离!!!!!!!!!!!于是我在网上找了很多资料,摘抄了一些,整理了一下,作为笔记&#…...

sublime配置(竞赛向)
我也想要有jiangly一样的sublime 先决条件 首先,到官网上下载最新的sublime4,然后在mingw官网上下载最新的mingw64 mingw64官网:左边菜单栏点击dowloads,然后选择MinGW-W64-builds(可能会有点慢)——然后有时候会变成选LLVM-minGW,接着选择…...

双向数据库迁移工具:轻松实现 MySQL 与 SQLite 数据互导
项目概述与作用 该项目的核心是实现 MySQL 和 SQLite 两种数据库之间的数据迁移工具。它能够轻松地将 MySQL 数据库中的数据导出为 SQLite 数据库文件,反过来也可以将 SQLite 数据库中的数据上传到 MySQL 数据库中。这个双向迁移工具非常适用于: 数据库备…...
oracle查询表空间信息
方式一,通过SQLPLUS查看,适用于无PLSQL等工具 sqlplus / as sysdba set line 200 set lines 200 col tablespace_name for a20 col SUM_SPACE(M) for a15 col USED_SPACE(M) for a15 col USED_RATE(%) for a15 col FREE_SPACE(M) for a15 SELEC…...

使用Python编写你的第一个算法交易程序
背景 Background 最近想学习一下量化金融,总算在盈透投资者教育(IBKRCampus)板块找到一篇比较好的算法交易入门教程。我在记录实践过程后,翻译成中文写成此csdn博客,分享给大家。 如果你的英语好可以直接看原文…...
点进HTML初步了解
写在前边 ##关于插件 ①简体中文 ②open-in-browser:自动在浏览器生成html页面; ③Auto Rename Tag:自动匹配标签; ④Live server:实现页面的实时刷新; ##关于快捷键: Ctrl / 用来注释…...

幸运的沈抖,进击的百度智能云
文|白 鸽 编|王一粟 AI对百度智能云的意义,可能远大于任何一家云计算厂商。 2022年5月,分管百度移动生态事业群组(MEG)的集团执行副总裁沈抖,转而担任百度智能云事业群组(ACG&…...
android广播实现PIN码设置
摘要:本文通过广播的方式调用系统设置PIN码的流程实现类似锁机的功能,可供开发人员在联网状态下后台推送消息进行锁机/解锁。有需要的同学可以参考PIN码的流程改为密码等其他形式。 1 定义一个广播接收器 广播action:android.intent.action…...

Mac 需要杀毒软件?
大部分 mac用户普遍认为 Apple mac 不受病毒和恶意软件的影响。这导致许多 Mac 用户误以为无需为 Mac 安装防病毒软件,但事实并非如此。 在这篇文章中,将深入探讨 Mac 安全性的细节,探索针对 Apple 设备的恶意软件类型,并为您…...

Java | Leetcode Java题解之第472题连接词
题目: 题解: class Solution {Trie trie new Trie();public List<String> findAllConcatenatedWordsInADict(String[] words) {List<String> ans new ArrayList<String>();Arrays.sort(words, (a, b) -> a.length() - b.length(…...

CUDA Graphs学习与实验
CUDA Graphs学习与实验 一.参考链接二.测试方案三.测试代码 CUDA图(CUDA Graphs)为CUDA引入了一种全新的工作提交模型。它允许将一系列操作(如内核启动)以图的形式表示,并通过依赖关系将这些操作连接起来。这种图的定义…...

第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...

label-studio的使用教程(导入本地路径)
文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...

【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...

OPENCV形态学基础之二腐蚀
一.腐蚀的原理 (图1) 数学表达式:dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一,腐蚀跟膨胀属于反向操作,膨胀是把图像图像变大,而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...

视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...