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

news2024/12/29 11:47:29

接着上文[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_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();}

    //返回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 thread
    EventLoop* 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_ctl
    int 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.coloradmin.cn/o/1669951.html

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

相关文章

onnx模型截取部分

这个是有需求的&#xff0c;比如有多个输入节点&#xff0c;我只用其中几个&#xff0c;或有多个输出节点&#xff0c;我只用其中几个。 比如这个输入&#xff0c;我们可以直接把transpose去掉&#xff0c;用类pytorch的N,C,H,W的格式输入。 还有如下输出&#xff1a; tran…

灵活QinQ

拓扑图 配置 sysname AR1 # interface GigabitEthernet0/0/0.10dot1q termination vid 10ip address 12.1.1.1 255.255.255.0 arp broadcast enable # interface GigabitEthernet0/0/0.20dot1q termination vid 20ip address 21.1.1.1 255.255.255.0 arp broadcast enable # …

LeetCode 题目 120:三角形最小路径和

❤️❤️❤️ 欢迎来到我的博客。希望您能在这里找到既有价值又有趣的内容&#xff0c;和我一起探索、学习和成长。欢迎评论区畅所欲言、享受知识的乐趣&#xff01; 推荐&#xff1a;数据分析螺丝钉的首页 格物致知 终身学习 期待您的关注 导航&#xff1a; LeetCode解锁100…

Java--初识类和对象

前言 本篇讲解Java类和对象的入门版本。 学习目的&#xff1a; 1.理解什么是类和对象。 2.引入面向对象程序设计的概念 3.学会如何定义类和创建对象。 4.理解this引用。 5.了解构造方法的概念并学会使用 考虑到篇幅过长问题&#xff0c;作者决定分多次发布。 面向对象的引入 J…

《QT实用小工具·六十三》QT实现微动背景,界面看似静态实则动态

1、概述 源码放在文章末尾 该项目实现了微动背景&#xff0c;界面看似静态实则动态&#xff0c;风动&#xff0c;幡动&#xff0c;仁者心动&#xff0c;所以到底是什么在动&#xff1f;哈哈~ 界面会偷偷一点一点改动文字颜色的颜色填充。 虽然是动态&#xff0c;但是慢到难以…

羊大师解析,如何遵守《节约用水条例》

羊大师解析&#xff0c;如何遵守《节约用水条例》 《节约用水条例》的实施旨在倡导和推动全社会形成节约用水的良好风尚&#xff0c;以应对日益严峻的水资源短缺问题。羊大师发现随着这一条例的深入实施&#xff0c;越来越多的人开始意识到节约用水的重要性&#xff0c;并积极…

HTML5实现简洁好看的个人主页,个人小站(多种风格附源码)

文章目录 1.烟灰主题个人主页1.1 个人主页界面1.2 个人信息界面1.3 兴趣爱好界面1.4 个人作品界面 2.紫霞主题个人主页2.1 个人主页界面2.2 个人信息界面2.3 兴趣爱好界面2.4 个人作品界面 3.墨夜主题个人主页3.1 个人主页界面3.2 个人信息界面3.3 兴趣爱好界面3.4 个人作品界面…

《小猫咪大城市》 48小时销量破40万套,一匹休闲游戏黑马诞生

易采游戏网5月13日消息&#xff0c;近日一款名为《小猫咪大城市》的游戏在Steam、Switch和Xbox平台上正式发售&#xff0c;凭借其独特的游戏设定和可爱的猫咪角色&#xff0c;迅速赢得了玩家们的喜爱。据官方宣布&#xff0c;游戏在发售后的短短48小时内&#xff0c;销量已经突…

汽车灯罩材料使用PMMA(亚克力)具有哪些优势?汽车车灯的灯罩如果破损破裂破洞了要怎么修复?

汽车灯罩材料使用PMMA&#xff08;亚克力&#xff09;具有哪些优势 首先&#xff0c;PMMA具有高透明度&#xff0c;其透光率可达92%以上&#xff0c;使得光线能够均匀、清晰地透过灯罩&#xff0c;为驾驶者提供明亮且均匀的照明效果&#xff0c;确保行车安全。 其次&#xff…

JavaEE初阶-多线程进阶1

文章目录 前言一、常见的锁策略1.1 乐观锁与悲观锁1.2 重量级锁与轻量级锁1.3 自旋锁与挂起等待锁1.4 可重入锁与不可重入锁1.5 公平锁与非公平锁1.6 互斥锁与读写锁 二、synchronized的优化策略2.1 锁升级2.2 锁消除2.3 锁粗化 前言 多线程进阶的内容在面试中容易考&#xff…

解决SpringBoot整合MyBatis和MyBatis-Plus,请求后不打印sql日志

问题发现 在整合springBootmyBatis时&#xff0c;发现请求不打印sql日志&#xff0c;示例代码如下&#xff1a; RestController public class MyController {AutowiredProductMapper productMapper;GetMapping("/test")public void test() {System.out.println(&qu…

【知识碎片】2024_05_12

本篇记录了两个代码题【字符个数的统计】和【多数元素】(下有一段快排的代码)&#xff0c;以及两个关于数组的选择题。 每日代码 字符个数统计 字符个数统计_牛客题霸_牛客网 统计ascall码在0~127内的字符出现过几次&#xff08;重复出不再计算&#xff09; #include <st…

快速入门:利用Go语言下载Amazon商品信息的步骤详解

概述 在这篇文章中&#xff0c;我们将深入探讨如何利用Go语言这一强大的工具&#xff0c;结合代理IP技术和多线程技术&#xff0c;实现高效下载Amazon的商品信息。首先&#xff0c;让我们来看看为什么选择Go语言作为开发网络爬虫的首选语言。 Go语言在网络开发中的特点 简洁…

Redis五大基本数据类型介绍及其使用场景

文章目录 1 String&#xff08;字符串&#xff09;应用场景 2 List&#xff08;列表&#xff09;应用场景 3 Set&#xff08;集合&#xff09;4 sorted set&#xff08;有序集合&#xff09;应用场景 5 hash&#xff08;哈希&#xff09;应用场景 Redis 是一个开源&#xff0c;…

Spring Boot集成zookeeper快速入门Demo

1.什么是zookeeper&#xff1f; Zookeeper 是一个开源的分布式协调服务&#xff0c;目前由 Apache 进行维护。Zookeeper 可以用于实现分布式系统中常见的发布/订阅、负载均衡、命令服务、分布式协调/通知、集群管理、Master 选举、分布式锁和分布式队列等功能。它具有以下特性…

webAPIs第二天

事件监听 什么是事件&#xff1f; 事件是在编程时系统内发生的动作或者发生的事情 比如用户在网页上单击一个按钮 什么是事件监听&#xff1f; 就是让程序检测是否有事件产生&#xff0c;一旦有事件触发&#xff0c;就立即调用一个函数做出响应&#xff0c;也称为 绑定事件或者…

常用的30个linux命令总结

1、常用30个命令总结 2、具体参数用法参考网站&#xff1a; Linux命令大全(手册) – 真正好用的Linux命令在线查询网站

Linux提权--第三方软件MYSQL数据库提权(WEB+本地)

免责声明:本文仅做技术交流与学习,非法搞事后果自负... 目录 靶场镜像: 过程: 手工: 下载mysql udf poc 进行编译. 进入数据库进行UDF导出 下载(上传) 创建do_system函数调用 探针(./LinEnum.sh),查找suid权限. 配合使用find调用执行 工具: 过程: 外连不上? 隧道出…

【网络安全入门】你必须要有的学习工具(附安装包)零基础入门到进阶,看这一篇就够了!

工欲善其事必先利其器 在新入门网络安全的小伙伴而言。这些工具你必须要有所了解。本文我们简单说说这些网络安全工具吧&#xff01; Web安全类 Web类工具主要是通过各种扫描工具&#xff0c;发现web站点存在的各种漏洞如sql注入、xss等。从而获取系统权限&#xff0c;常用的…

Node.js版本管理工具nvm安装

1.下载nvm https://github.com/coreybutler/nvm-windows/releaseshttps://github.com/coreybutler/nvm-windows/releases 2.安装nvm 双击打开下载好的压缩包解压出的文件 目录中不要有中文 这个是配置切换node版本后的存储位置 然后一路下一步就行了 3.安装并使用node 安装…