[muduo网络库]——muduo库三大核心组件之Channel类(剖析muduo网络库核心部分、设计思想)

接着上文[muduo网络库]——muduo库的Reactor模型(剖析muduo网络库核心部分、设计思想),接下来详细介绍一下这三大核心组件中的Channel类。
先回顾一下三大核心组件之间的关系。
在这里插入图片描述接着我们进入正题。

Channel

Channel类封装了一个 fd 、fd感兴趣事件events、该fd实际发生的事件revents。同时Channel类还提供了设置该fd的感兴趣事件,以及相应的回调函数。

重要成员变量

EventLoop *loop_;      //事件循环
const int fd_;         //fd:poller监听的对象 epoll_ctl
int events_;           //注册fd感兴趣的事件
int revents_;          //poller返回的具体发生的事件
//因为channel可以获得fd最终发生的具体事件revent,所以他负责回调
//他们都属于std::function<>类型,保管着可调用的函数 ;
ReadEventCallback readCallback_;
EventCallback writeCallback_;
EventCallback closeCallback_;
EventCallback errorCallback_;

重要成员函数

  • 设置回调函数对象
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);}
  • 设置fd相应的状态,update()相当于调用epoll_ctl
void enableReading() { events_ |= kReadEvent; update();} //相当于把读事件给events相应的位置位了
void disableReading() { events_ &= ~kReadEvent; update();}
void enableWriting() { events_ |= kWriteEvent; update();}
void disableWriting() { events_ &= ~kWriteEvent; update();}
void disableAll() { events_ = kNoneEvent; update();}

这里的update(),实际上就是调用了EventLoop里面的updateChannel(),进一步调用EPollPoller::updateChannel,在EventLoop里面我们还会进一步看到。

  • 同理还有remove(),最终也是调用了 EPollPoller::removeChannel
void Channel::remove()
{loop_->removeChannel(this);
}
  • 封装fd,fd感兴趣的事件以及实际发生的事件
int fd() const {return fd_;}
int events() const {return events_;}
int set_revents(int revt) { revents_=revt; }
  • fd得到poller通知以后,调用相应的方法来处理事件过程为handleEvent -> handleEventWithGuard -> 回调read_callback_/write_callback_/close_callback_/error_callback_
void handleEvent(TimeStamp receiveTime);
  • 还有一个Channel::tie()方法
    我们知道,当客户端正常断开TCP连接,IO事件会触发Channel中的设置的CloseCallback回调,但是用户代码在onClose()中有可能析构Channel对象,导致回调执行到一半的时候,其所属的Channel对象本身被销毁了。这时程序会出现问题。muduo的解决办法是提供Channel::tie(const boost::shared_ptr<void>&)这个函数,用于延长某些对象的生命期,使之长过Channel::handleEvent()函数。所以muduo库中的 TcpConnection采用了shared_ptr管理对象生命期。单说的意义并不大,所以在之后的剖析中遇到tie,会进一步介绍它的巧妙之处。
  • 处理事件
void Channel::handleEvent(TimeStamp receiveTime)
{if(tied_){std::shared_ptr<void> guard;guard = tie_.lock(); //提升if(guard){handleEventWithGuard(receiveTime);}}else{handleEventWithGuard(receiveTime);}
}void Channel::handleEventWithGuard(TimeStamp receiveTime)
{LOG_INFO("channel handleEvent revents:%d\n",revents_);// 连接断开,并且fd上没有可读数据(默认水平触发)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_();}}}

在这里,我们可以看出handleEvent中tie实际上这是一个弱指针,绑定到TcpConnection的共享指针 ,如果可以原来的弱指针,变成了强指针,这时候tie()的作用就表明了,延长了TcpConnection的生命周期,使之长过Channel::handleEvent(),保证了TcpConnection不被销毁,因此Channel中存了一个TcpConnection的弱指针,在处理事件的时候,lock将引用计数加1,Channel::handleEvent()来关闭连接时,guard变量依然持有一份TcpConnection。也就是说Channel不会在执行完Channel::handleEvent()之前被析构。这一点在这里可能还是有点稀里糊涂,在我们剖析到TcpConnection时,我还会再来分析一遍。如果难以理解,这里可以先记住有这个指针即可。

handleEventWithGuard根据revents_不同的值调用不同的回调函数,revents_的值是在channel->set_revents(events_[i].events);,由Poll检测是什么事件,给revents_赋相应的值,处理不同的事件。

本小节有关于Channel类核心部分就到此结束了,下一篇我们来看第二大核心部分 Poller/EpollPoller类。

本小节就到这里,下一篇我会对着三个核心组件进行一个详细的剖析介绍,希望有需要的小伙伴可以持续关注哦~
代码地址:https://github.com/Cheeron955/mymuduo/tree/master
最后附上Channel类源码

Channel.h

#pragma once#include "noncopyable.h"
#include "TimeStamp.h"#include <functional>
#include <memory>
/*
理清楚 EventLoop Channel,Poller之间的关系 他们在Reactor上面对应的Demultiplex 
Channel 理解为通道,封装了sockfd和其感兴趣的event,如EPOLLIN  EPOLLOUT事件
还绑定了poller返回的具体事件
*/class EventLoop;class Channel : noncopyable
{
public:using EventCallback = std::function<void()> ;using ReadEventCallback = std::function<void(TimeStamp)>;Channel(EventLoop *loop,int fd);//只用类型对应的指针,大小是固定的四个字节,不影响编译,所以直接可以前置声明EventLoop就可以~Channel();//fd得到poller通知以后,调用相应的方法来处理事件void handleEvent(TimeStamp receiveTime);//receiveTime变量,必须包含头文件//设置回调函数对象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_;}int set_revents(int revt) { revents_=revt; }//设置fd相应的状态 update()相当于调用epoll_ctlvoid enableReading() { events_ |= kReadEvent; update();} //相当于把读事件给events相应的位置位了void disableReading() { events_ &= ~kReadEvent; update();}void enableWriting() { events_ |= kWriteEvent; update();}void disableWriting() { events_ &= ~kWriteEvent; update();}void disableAll() { events_ = kNoneEvent; update();}//返回fd当前的事件状态bool isNoneEvent() const {return events_ == kNoneEvent;}bool isReading() const {return events_ & kReadEvent;}bool isWriting() const {return events_ & kWriteEvent;}int index() {return index_;}void set_index(int idx) { index_ = idx;}// one loop per threadEventLoop* onwerLoop() {return loop_;}void remove();
private:void update();void handleEventWithGuard(TimeStamp receiveTime);//成员变量static const int kNoneEvent; //fd的状态 没有感兴趣的static const int kReadEvent; //读static const int kWriteEvent; //写EventLoop *loop_;      //事件循环const int fd_;         //fd:poller监听的对象 epoll_ctlint events_;           //注册fd感兴趣的事件int revents_;          //poller返回的具体发生的事件int index_;std::weak_ptr<void> tie_;bool tied_;//因为channel可以获得fd最终发生的具体事件revent,所以他负责回调ReadEventCallback readCallback_;EventCallback writeCallback_;EventCallback closeCallback_;EventCallback errorCallback_;
};

Channel.cc

#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方法 在一个TcpConnection新连接创建的时候 调用
//TcpConnection->channel
void Channel::tie(const std::shared_ptr<void> &obj)
{tie_ = obj;tied_ = true;
}/*
当改变channel所表示的events事件后,update负责在poller里面更改fd相应的事件 epoll_ctl
EventLoop => ChannelList Poller
*/
void Channel::update()
{//通过channel所属的EventLoop,调用Poller相应的方法,注册fd的events事件loop_->updateChannel(this);
}//在channel所属的EventLoop中 ,把当前的channel删除掉
void Channel::remove()
{loop_->removeChannel(this);
}void Channel::handleEvent(TimeStamp receiveTime)
{if(tied_){std::shared_ptr<void> guard;guard = tie_.lock(); //提升if(guard){handleEventWithGuard(receiveTime);}}else{handleEventWithGuard(receiveTime);}
}void Channel::handleEventWithGuard(TimeStamp receiveTime)
{LOG_INFO("channel handleEvent revents:%d\n",revents_);// 连接断开,并且fd上没有可读数据(默认水平触发)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_();}}}

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

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

相关文章

【STM32 |程序实测】LED灯闪烁、LED灯流水线、蜂鸣器

LED闪烁&LED流水灯&蜂鸣器的面包板接线图&#xff0c;及对应程序示例 LED闪烁 面包板接线图如下 开启APB2时钟&#xff0c;并且在GPIOA上进行配置&#xff0c;推挽输出&#xff0c;引脚A0&#xff0c;50HZ速度 #include "stm32f10x.h" /…

[Linux][网络][网络层][IP协议]详细讲解

目录 0.基本概念1.IP协议头格式2.IP分片与组装1.为什么要分片&#xff1f;2.分片后谁来组装&#xff1f;3.这个分片操作传输层知道吗&#xff1f;4.如何识别报文和报文的不同&#xff1f;5.接收端&#xff0c;如何得知报文是独立的还是一个分片&#xff1f;6.如何区别哪些分片是…

【论文泛读|附源码】如何进行动力学重构? 神经网络自动编码器结合SINDy发现数据背后蕴含的方程

这一篇文章叫做 数据驱动的坐标发现与方程发现算法。 想回答的问题很简单&#xff0c;“如何根据数据写方程”。 想想牛顿的处境&#xff0c;如何根据各种不同物体下落的数据&#xff0c;写出万有引力的数学公式的。这篇文章就是来做这件事的。当然&#xff0c;这篇论文并没有…

数据结构--图。

在前面&#xff0c;我们学习了线性表和树&#xff0c;而接下来我们要学习的图相较于他们就更加复杂。 目录 一.图的有关概念 一.图的有关概念 1.定义 图(graph)G由两个集合V和E组成&#xff0c;记为G&#xff08;VE)。V是顶点的有穷非空集合;E是边的集合,边是V中顶点的无序对…

【Linux】传输文件,补充:VMware中Linux系统无法连接网络的解决方法

Linux系统可以和其他系统之间进行传输文件&#xff0c;只要通过ssh连接成功以后&#xff0c;就能进行文件传输。 Linux系统也可以通过URL规则和网页之间进行传输文件&#xff08;即上传/下载&#xff09;。 1、Linux系统之间传输文件&#xff1a;scp centos7自带ssh服务&…

FPGA+炬力ARM实现VR视频播放器方案

FPGA炬力ARM方案&#xff0c;单个视频源信号&#xff0c;同时驱动两个LCD屏显示&#xff0c;实现3D 沉浸式播放 客户应用&#xff1a;VR视频播放器 主要功能&#xff1a; 1.支持多种格式视频文件播放 2.支持2D/3D 效果实时切换播放 3.支持TF卡/U盘文件播放 4.支持定制化配置…

36.Docker-Dockerfile自定义镜像

镜像结构 镜像是将应用程序及其需要的系统函数库、环境、配置、依赖打包而成。 镜像是分层机构&#xff0c;每一层都是一个layer BaseImage层&#xff1a;包含基本的系统函数库、环境变量、文件系统 EntryPoint:入口&#xff0c;是镜像中应用启动的命令 其他&#xff1a;在…

QT-小项目:连接MY SQL数据库实现登录(下一章实现登录注册账号和忘记密码功能)

一、环境准备 1、下载MYSQL 64位&#xff0c;安装完成&#xff0c;制作简易数据库教程如下&#xff1a; MY SQL安装 2、QT 编译器使用 二、实现工程目录&#xff08;基于上一章基础上&#xff09; 三、源程序增加内容如下&#xff1a; login.cpp 增加头文件&#xff1a; #in…

《TAM》论文笔记(上)

原文链接 [2005.06803] TAM: Temporal Adaptive Module for Video Recognition (arxiv.org) 原文代码 GitHub - liu-zhy/temporal-adaptive-module: TAM: Temporal Adaptive Module for Video Recognition 原文笔记 What&#xff1a; TAM: Temporal Adaptive Module for …

内网安全综合管理系统是什么 | 好用的内网安全管理系统有哪些

内网安全综合管理系统是指一种集成终端管理、网络管理、内容管理、资产管理等功能的综合性安全管理系统。它主要对内网上的主机进行统一安全管理&#xff0c;包括对网络主机用户操作实施监督控制&#xff0c;并对主机中的安全软件&#xff08;如主机入侵监测系统、主机防火墙和…

5 Spring 事务管理

目录 1.概述 2.事务特性&#xff1a;ACID 3.Spring 框架的事务管理支持两种方式 编程式事务 申明式事务 4.Spring 事务管理 API 事务管理器接口 Spring 的回滚方式 事务定义接口 事务的四种隔离级别 事务的七种传播行为 5.事务注解例子&#xff1a; Transactianal…

springboot+vue+mybatis警情高发智能灯箱+PPT+论文+讲解+售后

时代在飞速进步&#xff0c;每个行业都在努力发展现在先进技术&#xff0c;通过这些先进的技术来提高自己的水平和优势&#xff0c;警情高发智能灯箱当然不能排除在外。警情高发智能灯箱是在实际应用和软件工程的开发原理之上&#xff0c;运用微信开发者、java语言以及SpringBo…

python:做柱状图

import matplotlib.pyplot as plt # 数据 categories [A, B, C, D] values [23, 45, 56, 78] # 创建柱状图 plt.bar(categories, values) # 添加标题和标签 plt.title(柱状图示例) plt.xlabel(类别) plt.ylabel(数值) # 显示图形 plt.show() D:\software\新建文件夹\python\L…

力扣每日一题- 给植物浇水 II -2024.5.9

力扣题目&#xff1a;给植物浇水 II 题目链接: 2105.给植物浇水 II 题目描述 代码思路 根据题目内容&#xff0c;使用双指针从左右两边同时向中间移动&#xff0c;模拟浇水过程即可。 代码纯享版 class Solution {public int minimumRefill(int[] plants, int capacityA, …

java 文件表创建及前后端使用

表结构task_file 前端具体到业务表单 <el-form-item label"任务附件" prop"taskAttachment"><el-upload ref"upload" accept".jpg, .png, .txt, .xlsx, .doc, .docx, .xls, .pdf, .zip, .rar":action"upload.url" …

Go-Zero自定义goctl实战:定制化模板,加速你的微服务开发效率(四)

前言 上一篇文章带你实现了Go-Zero和goctl&#xff1a;解锁微服务开发的神器&#xff0c;快速上手指南&#xff0c;本文将继续深入探讨Go-Zero的强大之处&#xff0c;并介绍如何使用goctl工具实现模板定制化&#xff0c;并根据实际项目业务需求进行模板定制化实现。 通过本文…

videosapi开发微信管理系统

获取登录二维码&#xff1a; export interface Request {/*** 设备ID&#xff0c;首次登录传空&#xff0c;之后传接口返回的appId*/appId?: string;/*** 代理IP 格式&#xff1a;socks5://username:password123.2.2.2*/proxyIp?: string;/*** 地区*/regionId: string;[prop…

宋仕强论道之新质生产力

宋仕强论道之新质生产力&#xff0c;宋仕强说当前5G通信、人工智能、万物互联、工业互联网、数字经济、新能源技术和产业等领域正蓬勃发展&#xff0c;成为未来经济增长的重要推动力&#xff0c;也是目前提倡的新质生产力的重要组成部分。而这些领域的发展都离不开数据的采集、…

React使用Outlet实现路由跳转时局部刷新页面

Outlet是react-router-dom插件的一个组件&#xff0c;首先需要安装react-router-dom插件&#xff1a; cnpm i react-router-dom --save 官方文档 应该在父路由元素中用来渲染其子路由元素。这允许在渲染子路由时显示嵌套的 UI。如果父路由完全匹配&#xff0c;则将渲染子索引…

Flutter 引入webview_windows插件,在已经使用$PATH 中的 nuget.exe情况下,windows端构建失败

报错 PS F:\xx\xxxx> flutter run -d windows Flutter assets will be downloaded from https://storage.flutter-io.cn. Make sure you trust this source! Launching lib\main.dart on Windows in debug mode... E:\Some software\Visual Studio\VS 2022\MSBuild\M…