当前位置: 首页 > news >正文

Muduo库源码剖析(一)——Channel

Muduo库源码剖析(一)——Channel

说明

本源码剖析是在muduo基础上,保留关键部分进行改写分析。

要点总结

事件分发器 event dispatcher中最重要的两个类型 channelPoller

Channel可理解为通道,poller往通道传输数据(事件发生情况)。

EventLoop包含多个channel一个 Poller

Channel相当于是对socket事件处理封装,包含了socket的详细信息,scoket以及感兴趣的事件都在channel里;

channel是muduo库负责注册读写事件的类,并保存了fd读写事件发生时调用的回调函数,如果poll/epoll有读写事件发生则将这些事件添加到对应的通道中。

  • 一个channel对应唯一EventLoop,一个EventLoop可以有多个channel

  • Channel类不负责fd的生存期,fd的生存期是由socket决定的,断开连接关闭描述符。

  • 当有fd返回读写事件时,调用提前注册的回调函数处理读写事件

  • 头文件中只给类的前置声明,而在源文件中再给出头文件包含,因为源文件会被编程动态库.so, 减少对外暴露

  • weak_ptr 用于观察绑定对象的状态,并且可以尝试提升为shared_ptr

Channel这个模块对应Reactor模型上的 Demultiplex (多路复用器)

在这里插入图片描述

重点代码详解

// Channel.h
#pragma once#include "noncopyable.h"
#include "Timestamp.h"#include <functional>
#include <memory>// 头文件中只给类的前置声明,而在源文件中再给出头文件包含
// 因为源文件会被编程动态库.so, 减少对外暴露
class EventLoop;class Channel : noncopyable
{
public:using EventCallback = std::function<void()>;using ReadEventCallback = std::function<void(Timestamp)>;Channel(EventLoop *loop, int fd);~Channel();// fd得到poller通知后调用其处理事件void handleEvent(Timestamp recevieTime);void setReadCallback(ReadEventCallback cb) { readCallback_ = std::move(cb); }void setWriteCallback(EventCallback cb) { writeCallback_ = std::move(cb); }void setCloseCallback(EventCallback cb) { closeCallback_ = std::move(cb); }void setErrorCallback(EventCallback cb) { errorCallback_ = std::move(cb); }// 防止当Channel的所有者被手动remove掉时,Channel 仍在执行回调void tie(const std::shared_ptr<void>&); // 检测资源存活状态int fd() const { return fd_; }int events() const { return events_; }void set_revents(int revt) { revents_ = revt; }// 设置fd相应的事件状态// enableReading 让fd对读事件感兴趣// update()底层也是调用 epoll_ctlvoid enableReading() { events_ |= kReadEvent; update(); }void disableReading() { events_ &= ~kReadEvent; update(); }void enableWriting() { events_ |= kWriteEvent; update(); }void disableWriting() { events_ &= ~kWriteEvent; update(); }void disableAll() { events_ = kNoneEvent; }int index() { return index_; }void set_index(int idx) { index_ = idx; }// oneloop per thread// 当前Channel所属的eventloopEventLoop* ownerLoop() {return loop_;}void remove();private:void update();void handleEventWithGuard(Timestamp recvTime);static const int kNoneEvent; // 感兴趣的事件类型,该变量表示不感兴趣任何事件static const int kReadEvent; static const int kWriteEvent; EventLoop *loop_; // 事件循环const int fd_;  //fd, poller监听的对象int events_;    // 注册感兴趣的事件int revents_;   // poller返回的具体发生的事件类型(可读?可写?)int index_;std::weak_ptr<void> tie_; // 用于观察shared_ptr的状态bool tied_;// 因为Channel里能得知fd最终发生的具体事件revents_// 故它负责调用对应的回调ReadEventCallback readCallback_;EventCallback writeCallback_;EventCallback closeCallback_;EventCallback errorCallback_;};
// Channel.cpp
#include "Channel.h"
//#include "EventLoop.h"
#include "Logger.h"#include <sys/epoll.h>const int Channel::kNoneEvent = 0; 
const int Channel::kReadEvent = EPOLLIN | EPOLLPRI; 
const int Channel::kWriteEvent = EPOLLOUT;Channel::Channel(EventLoop *loop, int fd): loop_(loop), fd_(fd), events_(0), revents_(0), index_(-1), tied_(false)
{
}Channel::~Channel()
{    
}// ??channel的tie方法什么时候调用过
void Channel::tie(const std::shared_ptr<void> &obj)
{tie_ = obj;tied_ = true;
}// 当改变Channel所表示fd的events事件后,update负责在poller里更改fd相应事件--epoll_ctl
// EventLoop has ChannelList Poller
void Channel::update()
{// 通过Channel所属的EventLoop,调用Poller的相应方法,注册fd的events事件// [TODO]// loop_->updateChannel(this);
}// 在Channel所属的EventLoop中,把当前Channel删除
void Channel::remove()
{// [TODO]// loop_->removeChannel(this);
}// fd得到poller通知后处理事件
void Channel::handleEvent(Timestamp receiveTime)
{if(tied_) // 资源存活{std::shared_ptr<void> guard = tie_.lock();if(guard){handleEventWithGuard(receiveTime);}}else {handleEventWithGuard(receiveTime);}
}// 根据Poller通知的Channel发生的具体事件,调用相应的回调
void Channel::handleEventWithGuard(Timestamp receiveTime)
{LOG_INFO("channel handleEvent revents:%d\n", revents_);// EPOLLHUP 表示读写都关闭if((revents_ & EPOLLHUP) && !(revents_ & EPOLLIN)){if(closeCallback_){closeCallback_();}}if(revents_ & EPOLLERR){if(errorCallback_){errorCallback_();}}if(revents_ & (EPOLLIN | EPOLLPRI)){if(readCallback_){readCallback_(receiveTime);}}if(revents_ & EPOLLOUT){if(writeCallback_){writeCallback_();}}}

参考资料

  1. 陈硕. 《LInux多线程服务端编程》

  2. 施磊. 《手写C++Muduo网络库》

相关文章:

Muduo库源码剖析(一)——Channel

Muduo库源码剖析(一)——Channel 说明 本源码剖析是在muduo基础上&#xff0c;保留关键部分进行改写分析。 要点总结 事件分发器 event dispatcher中最重要的两个类型 channel 和 Poller Channel可理解为通道&#xff0c;poller往通道传输数据(事件发生情况)。 EventLoop…...

Java多线程:定时器Timer

前言 定时/计划功能在Java应用的各个领域都使用得非常多&#xff0c;比方说Web层面&#xff0c;可能一个项目要定时采集话单、定时更新某些缓存、定时清理一批不活跃用户等等。定时计划任务功能在Java中主要使用的就是Timer对象&#xff0c;它在内部使用多线程方式进行处理&am…...

设计模式---装饰模式

目录 介绍 实现 优缺点 装饰模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其结构。这种类型的设计模式属于结构型模式&#xff0c;它是作为现有类的一个包装。这种模式创建了一个装饰类&#xff0c;用来包装原有…...

跨时钟域传输数据——单bit和多bit信号(总结)

文章目录前言一、慢时钟域到快时钟域1、单bit信号2、多bit信号二、快时钟域到慢时钟域1、单bit信号2、多bit信号三、多bit信号跨时钟域传输1、多个信号合并2、多周期路径 Multi-cycle Path/MCP3、使用格雷码4、使用异步FIFO5、使用DMUX电路结构6、握手信号传输四、简答题1、跨时…...

高并发下如何保证接口幂等

文章目录 1. insert前先select2. 加悲观锁3. 加乐观锁4. 加唯一索引5. 建防重表6. 根据状态机7. 加分布式锁8. 获取token接口幂等性问题,对于开发人员来说,是一个跟语言无关的公共问题。本文分享了一些解决这类问题非常实用的办法,绝大部分内容我在项目中实践过的,给有需要…...

Retrofit源码分析小结

Retrofit源码分析&小结 简介 Retrofit是对Okhttp网络请求的二次封装&#xff0c;通过注解动态代理的方式&#xff0c;简化了Okhttp的使用&#xff0c;使得通过简单的配置就可以像调用接口一样去请求网络接口&#xff1b;除此之外Retrofit还支持RxJava和kotlin的协程 基本…...

【从零开始学习 UVM】11.4、UVM Register Layer —— UVM Register Model 实战项目(RAL实战,交通灯为例)

文章目录 DesignInterfaceRegister Model ExampleRegister EnvironmentAPB Agent ExampleTestbench EnvironmentSequencesTest在之前的几篇文章中,我们已经了解了寄存器模型是什么以及如何使用它来访问给定设计中的寄存器。现在让我们看一个完整的例子,展示如何为给定设计编写…...

session和token的登录机制

做登录的时候遇到了token &#xff0c;web和smtp的登录情况&#xff0c;这里 记录一下我所学习的两种登录方式&#xff0c;一种是token &#xff0c;一种是session session 登录机制 当用户请求登录接口进行登录服务端 获得登录的信息&#xff0c;从而在数据库中查到相应的用…...

大厂研发成本大曝光,研发占大头

近日&#xff0c;腾讯发布《2022 年腾讯研发大数据报告》&#xff0c;披露了 2022 年腾讯在研发投入、研发效能、开源协同等方面的重要数据。 《报告》显示&#xff0c;2022 年腾讯内部研发人员占比达到 74%&#xff0c;这意味着&#xff0c;平均每四个腾讯员工中&#xff0c;…...

python爬虫第一节基础概念

爬虫是一种自动化抓取互联网上数据的技术。在网络信息爆炸的今天&#xff0c;爬虫技术已经成为数据获取和信息分析的重要手段。本文将详细介绍爬虫的基础知识和操作&#xff0c;帮助初学者快速入门。 一、爬虫的基本原理 爬虫的基本原理是通过网络请求获取网页源代码&#xf…...

web学习---Vue---笔记(1)

该笔记是记录尚硅谷的Vue学习视频的笔记&#xff0c;视频地址为&#xff1a;学习视频地址 初始Vue Vue组件化的特点 组件化声明式编码虚拟DOMDiff算法&#xff0c;尽量复用DOM节点 H5的组件&#xff0c;是把某一个模块封装&#xff0c;里面写HTML\CSS\JS等&#xff0c;算是一…...

【前端面试题——微信小程序】

目录1.请谈谈wxml与标准的html的异同&#xff1f;2.请谈谈WXSS和CSS的异同&#xff1f;3.请谈谈微信小程序主要目录和文件的作用&#xff1f;4.请谈谈小程序的双向绑定和vue的异同&#xff1f;5.简单描述下微信小程序的相关文件类型&#xff1f;6.微信小程序有哪些传值(传递数据…...

gpt模型训练-gpt3模型详解

训练一个GPT模型需要大量的数据集和计算资源。在这里&#xff0c;我提供一些较为通用的训练步骤以供参考&#xff1a; 获取数据集 首先需要收集一些数据集&#xff0c;数据集建议获取大型的常用文本数据集。常见的例如维基百科、各种在线文章、小说、论文等&#xff0c;数据集…...

vue尚品汇商城项目-day04【27.分页器静态组件(难点)】

文章目录27.分页器静态组件&#xff08;难点&#xff09;本人其他相关文章链接27.分页器静态组件&#xff08;难点&#xff09; 难点&#xff1a; 考虑点1&#xff1a;为啥需要分页呢&#xff1f; 答案&#xff1a;按需加载 考虑点2&#xff1a;分页器展示&#xff0c;需要哪…...

使用SeaFile搭建私有云盘并公网访问【cpolar内网穿透】

文章目录1. 前言2. SeaFile云盘设置2.1 Owncould的安装环境设置2.2 SeaFile下载安装2.3 SeaFile的配置3. cpolar内网穿透3.1 Cpolar下载安装3.2 Cpolar的注册3.3 Cpolar云端设置3.4 Cpolar本地设置4. 公网访问测试5. 结语1. 前言 现在我们身边的只能设备越来越多&#xff0c;各…...

蓝桥杯第26天(Python)考前挣扎

题型&#xff1a; 1.思维题/杂题&#xff1a;数学公式&#xff0c;分析题意&#xff0c;找规律 2.BFS/DFS&#xff1a;广搜&#xff08;递归实现&#xff09;&#xff0c;深搜&#xff08;deque实现&#xff09; 3.简单数论&#xff1a;模&#xff0c;素数&#xff08;只需要…...

WuThreat身份安全云-TVD每日漏洞情报-2023-04-04

漏洞名称:RSA NetWitness Platform 内存损坏漏洞 漏洞级别:中危 漏洞编号:CVE-2022-47529,CNNVD-202303-2419 相关涉及:RSA NetWitness Platform 12.2之前版本 漏洞状态:POC 参考链接:https://tvd.wuthreat.com/#/listDetail?TVD_IDTVD-2023-07193 漏洞名称:EyouCms <1.5.…...

【C++】Step by Step的格式化代码风格是这样的吗?

文章目录前言一、依赖二、配置总结前言 本节从0开始讲解如何格式化自己的代码风格&#xff0c;使用vscode插件来完成&#xff0c;本节的所有配置都会在星球同步哦~ 一、依赖 本次使用的是clang-format插件&#xff0c;具体安装比较简单&#xff1a; mac系统&#xff1a; br…...

aspnet030高校学生团体管理系统sqlserver

net030高校学生团体管理系统 . 1.用户基本信息管理模块&#xff1a;录入、修改、删除、查询、统计、打印等功能 2.学生成绩管理模块&#xff1a;录入、修改、删除、查询、统计、打印等功能 3.学生团体信息管理模块&#xff1a;录入、修改、删除、查询、统计、打印等功能 4.教…...

学习HM微博项目第10天

步骤&#xff1a;发微博12-表情键盘06-点击表情 -> 发微博13-表情键盘07-插入表情和封装textView -> 发微博14-表情键盘08-长按表情 -> 发微博15-表情键盘09-最近表情 -> 发微博16-表情键盘10-最近表情完善 发微博12-表情键盘06-点击表情 APP的演示动画&#xff…...

 轻松构建可信的智能代理:AgentScope框架介绍

什么是 AgentScope&#xff1f; AgentScope 是一个生产就绪的、易于使用的代理框架&#xff0c;它提供了与不断增强的模型能力相兼容的基本抽象&#xff0c;并且内置支持微调功能。我们为越来越具代理性的语言模型&#xff08;LLMs&#xff09;设计这种框架&#xff0c;方法是…...

Delphi 防破解与加壳保护:让你的软件不被逆向、不被篡改

不管你做的是登录器、工具软件、收费系统&#xff0c;只要不想被人随便破解、篡改、去广告&#xff0c;这一篇必须吃透。一、为什么要做软件保护&#xff1f;你的登录器被人破解&#xff0c;随便跳过验证直接进游戏你的收费工具被人去广告、改内存、无限试用关键配置、账号密码…...

SHAP多分类可视化报错?手把手教你用shap.summary_plot搞定Iris数据集(附正确代码)

SHAP多分类可视化报错&#xff1f;手把手教你用shap.summary_plot搞定Iris数据集&#xff08;附正确代码&#xff09; 最近在复现SHAP多分类可视化时&#xff0c;不少同行反馈遇到了"TypeError: only integer scalar arrays can be converted to a scalar index"的报…...

OpenClaw隐私保护方案:百川2-13B量化模型本地处理敏感数据

OpenClaw隐私保护方案&#xff1a;百川2-13B量化模型本地处理敏感数据 1. 为什么我们需要本地化的隐私保护方案 去年我在处理一批客户调研数据时&#xff0c;曾不小心将包含身份证号的Excel表格上传到了某云端OCR服务。虽然及时删除了文件&#xff0c;但那种"数据已经不…...

基于相关分析法与M序列的系统脉冲响应抗噪辨识技术

1. 噪声环境下的系统辨识挑战 在工业控制和信号处理领域&#xff0c;系统辨识就像给一个黑箱系统做"体检"。想象一下医生通过观察病人对各种刺激的反应来判断病情&#xff0c;工程师们也是通过分析系统对输入信号的响应来了解系统特性。但现实世界从来不是安静的实验…...

嵌入式开发常见问题与调试技巧

嵌入式开发中的常见问题与解决方案1. 开发过程中的典型挑战1.1 软件层面的常见问题在嵌入式软件开发中&#xff0c;bug的出现是不可避免的。开发者需要掌握系统化的调试方法&#xff1a;状态机编程&#xff1a;对于复杂的控制逻辑&#xff0c;采用状态机设计模式可以显著提高代…...

【GitHub项目推荐--Carbonyl:终端里的 Chromium 图形浏览器】⭐⭐⭐⭐⭐

简介 Carbonyl​ 是一个基于 Chromium 引擎、专为终端&#xff08;Terminal&#xff09;环境构建的开源图形浏览器。它并非 Lynx 那样的纯文本浏览器&#xff0c;而是通过 Unicode 块字符和 ANSI 颜色&#xff0c;将网页以像素级图形的方式渲染在命令行窗口中。该项目最初源于…...

Vue项目实战:5分钟搞定ECharts与高德地图(AMap)的完美结合

Vue项目实战&#xff1a;5分钟实现ECharts与高德地图的深度整合 最近在开发一个物流数据可视化平台时&#xff0c;遇到了一个典型需求&#xff1a;如何在地图上动态展示全国各区域的订单流向&#xff1f;经过反复尝试&#xff0c;发现ECharts与高德地图的组合是最佳解决方案。本…...

掌握Dynamic-DataSource注解与事务传播:MANDATORY模式终极指南

掌握Dynamic-DataSource注解与事务传播&#xff1a;MANDATORY模式终极指南 【免费下载链接】dynamic-datasource dynamic datasource for springboot 多数据源 动态数据源 主从分离 读写分离 分布式事务 项目地址: https://gitcode.com/gh_mirrors/dy/dynamic-datasource …...

Mac系统Jmeter从零到一:接口压力测试实战入门

1. 为什么选择Jmeter做接口压力测试 最近接手一个需求&#xff1a;需要对某个关键接口进行100次循环压力测试&#xff0c;检查是否存在偶发性返回数据为空的问题。作为Mac用户&#xff0c;我第一时间想到了Jmeter这个工具。你可能好奇为什么不用Postman或者curl脚本&#xff1…...