IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> muduo中封装fd和事件的Channel类 -> 正文阅读

[C++知识库]muduo中封装fd和事件的Channel类

muduo库中的Channel类封装了需要监听的fd以及fd感兴趣的事件events和返回的事件revents
在这里插入图片描述

TcpServer相当于是muduo库提供给外部编写服务器程序的一个类,相当于一个工具箱,把muduo库有关服务器的编程相关的东西,包括反应堆,事件分发器,事件回调都打包一块了

我们看看它的成员变量
在这里插入图片描述
EventLoop就是开启epoll_wait事件循环,可以理解成epoll,我们看看EventLoop具体怎么实现的
在这里插入图片描述
EventLoop的成员变量
在这里插入图片描述
Poller相当于是epoll抽象的概念,用Poller当做基类,在派生类中实现了poll和epoll,poll和epoll负责事件分发

此外,epoll还需要监听socket。我们注意到,在EventLoop类内除了编译器内置类型,自定义类型只有两个,Poller和Channel

我们看看Channel的一些成员变量
在这里插入图片描述
Channel包含了监听的sockfd,感兴趣的事件和发生的事件
在这里插入图片描述
Eventloop是事件循环(epoll_wait),相当于事件分发器,包含了Poller(epoll)和Channel(epoll监听的sockfd,以及它对应的事件和最终发生的事件),对应Reactor模型上的多路事件分发器Demultiplex

一个channel属于一个EventLoop,一个EventLoop对应多个channel,一个channel封装了一个fd和这个fd对应的感兴趣的事件以及发生的事件

Channel要向Poller注册感兴趣事件,Poller通知Channel发生的事件,Channel得到关于fd的事件通知会调用预置的回调操作

一个线程对应一个eventloop,一个eventloop有一个poller,一个poller可以监听多个channel,每个channel属于一个eventloop,一个eventloop有很多channel,这就是多路;一个线程监听多路fd

Channel.h

#pragma once

#include "noncopyable.h"
#include "Timestamp.h"

#include <functional>
#include <memory>

class EventLoop;

/**
 *  Channel理解为通道,封装了sockfd和其感兴趣的事件,如EPOLLIN、EPOLLOUT事件,还包括Poller返回的具体事件
 */

class Channel : noncopyable{
    public:
        using EventCallBack =  std::function<void()>;
        using ReadEventCallBack = std::function<void(Timestamp)>;

        Channel(EventLoop* loop, int fd);         // loop是指针,编译阶段能确定大小,不需要知道EventLoop的定义,无需包含头文件
        ~Channel();
        
        // 得到poller通知以后,处理事件
        void handleEvent(Timestamp receiveTime);  // 由于receiveTime是对象,不是指针,编译阶段需要确定对象的大小,所以只有Timestamp的声明不够,需要有类定义,即需要包含头文件

        // 设置回调操作 不需要形参的局部对象,将左值转换成右值
        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 errorCloseCallBack(EventCallBack cb){ errorCallBack_ = std::move(cb); }

        // 防止channel被手动remove,channel还在执行回调操作,处理fd上的数据
        void tie(const std::shared_ptr<void>&);
        int fd() const { return fd_; }

        int events() const{ return events_; }
        // 设置revents_,poller(poll/epoll)在监听fd,发生事件后调用set_revents告诉channel发生了什么事件
        void set_revents(int revt){ revents_ = revt; }
        
        void enableReading(){
            events_ |= kReadEvent;  // 相当于是添加感兴趣的事件,把events_中和read相关的位 置1
            update();               // epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &events_)
        }
        void disableReading(){
            events_ &= ~kReadEvent; // 删除读事件,取反后&,会把events_中和read相关的位 置0
            update();
        }
        void enableWriting(){ events_ |= kWriteEvent; update(); }
        void disableWriting(){ events_ &= ~kWriteEvent; update(); }
        void disableAll(){ events_ = kNoneEvent; update(); }

        bool isNoneEvent() const{ return events_ == kNoneEvent; }
        bool isWritingEvent() const{ return events_ & kWriteEvent; }
        bool isReadingEvent() const{ return events_ & kReadEvent; }

        int index(){ return index_; }
        void set_index(int idx){ index_ = idx; }

        // one thread per thread
        // 一个channel对应一个EventLoop,一个EventLoop对应多个channel,一个channel包含一个fd以及这个fd对应的感兴趣的事件以及发生的事件
        EventLoop* ownerLoop(){ return loop_; }

        // 用于删除channel
        void remove();

    private:
        void update();
        void handleEventWithGaurd(Timestamp receiceTime);

        // 表示当前fd的状态,对什么事件感兴趣
        static const int kNoneEvent;
        static const int kReadEvent;
        static const int kWriteEvent;

        EventLoop* loop_;  // 归属于哪个事件循环 epoll_wait
        const int fd_;     // Poller监听的对象
        int events_;       // 注册fd感兴趣的事件
        int revents_;      // Poller返回的具体发生的事件
        int index_;

        std::weak_ptr<void> tie_;  // 绑定自己,防止channel被手动remove后,我们还在使用channel
        bool tied_;

        // Poller通知Channel发生的事件会放在revents_,所以Channel负责调用具体事件的回调操作
        ReadEventCallBack readCallback_;
        EventCallBack writeCallBack_;
        EventCallBack closeCallBack_;
        EventCallBack errorCallBack_;
};

Channel类析构函数源码如下:

Channel::~Channel()
{
  assert(!eventHandling_);  // handleEventWithGuard调用会到函数处理事件的时候会使用,保证没有正在被处理的回调操作
  assert(!addedToLoop_);    // addedToLoop_在update的时候会被置为true,remove的时候被置为false,assert保证channel要么没使用过,要么被remove

  // 由于当前channel属于某个eventloop,判断当前channel是否在当前事件循环loop_的线程析构
  if (loop_->isInLoopThread())
  {
    assert(!loop_->hasChannel(this)); // 判断当前channel是否属于当前的事件循环loop_
  }
}

在这里插入图片描述
在这里插入图片描述
由于muduo源码中的Channel析构函数没有任何释放资源的操作,我们也就不写了

Channel::~Channel(){}

得到Poller通知后,Channel需要调用,处理事件

handleEvent源码如下:

void Channel::handleEvent(Timestamp receiveTime)
{
  std::shared_ptr<void> guard;
  if (tied_)              // weak_ptr tie_用于监听当前channel
  {
    guard = tie_.lock();  // 把weak_ptr tie_通过lock提升成shared_ptr
    if (guard)            // 查看tie_观察的对象是否还存活(资源是否被释放)
    {
      handleEventWithGuard(receiveTime);  // 观察的对象还存活,就调用事件处理函数
    }
  }
  else
  {
    // 如果没有执行tie()方法,tied_就是false,则直接调用事件处理函数
    handleEventWithGuard(receiveTime);
  }
}

那tie方法什么时候执行?有什么作用呢?后面再补

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(){}

void Channel::tie(const std::shared_ptr<void>& obj){
    //  weak_ptr tie_ 观察 shared_ptr obj
    tie_ = obj;
    tied_ = true;
}

/*
  向Poller(poll/epoll)更新fd感兴趣的事件,就是epoll_ctl
  EventLoop包含Channel、Poller,在Channel中的update方法需要更新epoll内核事件表中fd感兴趣的事件,
  然而epoll内核事件表需要由Poller操作,所以Channel告诉EventLoop,需要Poller更新epoll内核事件表
*/
void Channel::update(){
    // 通过Channel所属的EventLoop,调用Poller的方法,注册fd的events事件
    loop_->updateChannel(this);
}

void Channel::remove(){
    // 在当前channel所属的eventloop中删除当前channel
    loop_->removeChannel(this);
}

// 得到poller通知以后,处理事件
void Channel::handleEvent(Timestamp receiveTime){
    if(tied_){
        std::shared_ptr<void> guard = tie_.lock();
        if(guard){
            handleEventWithGaurd(receiveTime);
        }
    }else{
        handleEventWithGaurd(receiveTime);
    }
}

// 根据接收到的事件,执行对象的回调函数处理
void Channel::handleEventWithGaurd(Timestamp receiveTime){
    // LOG_INFO("in function : %s\n", __FUNCTION__);
    // LOG_INFO("line : %s\n", __LINE__);

    LOG_INFO("channel handleEvent revents: %d\n", revents_);

    if((revents_ & EPOLLHUP) && !(revents_ & EPOLLIN)){
        // 读写都关闭 && 没有读事件,表示出问题了
        if(closeCallBack_){
            // 回调函数对象不空,执行回调
            closeCallBack_(); 
        }
    }

    if(revents_ & EPOLLERR){
        if(errorCallBack_){
            errorCallBack_(); 
        }
    }

    if(revents_ & (EPOLLIN | EPOLLPRI | EPOLLRDHUP)){
        if(readCallBack_){
            readCallBack_(receiveTime); 
        }
    }

    if(revents_ & EPOLLOUT){
        if(writeCallBack_){
            writeCallBack_(); 
        }
    }
}
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-26 11:24:29  更:2022-04-26 11:24:58 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 22:20:41-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码