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 (多路复用器)

Muduo库源码剖析(一)——Channel_第1张图片

重点代码详解

// Channel.h
#pragma once

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

#include 
#include 

// 头文件中只给类的前置声明,而在源文件中再给出头文件包含
// 因为源文件会被编程动态库.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_ctl
    void 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所属的eventloop
    EventLoop* 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 

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,网络编程,C/C++,c++,服务器)