• muduo源码剖析之channel通道类


    简介

    channel是muduo中的事件分发器,它只属于一个EventLoop,Channel类中保存着IO事件的类型以及对应的回调函数,每个channel只负责一个文件描述符,但它并不拥有这个文件描述符。channel是在epoll和TcpConnection之间起沟通作用,故也叫做通道,其它类通过调用channel的setCallbcak来和建立channel沟通关系。

    Channel类主要作用:

    1. 将文件描述符(可能是socket类型,eventfd类型,timefd类型,signalfd类型)封装,通过该类设置各种事件的回调函数(例如读回调,写回调,关闭回调等)。
    2. 可以设置自己的监听事件类型,然后根据该类型更新poller对象(epoll或者poll)对该类的操作(例如添加,修改,删除操作)。
    3. 根据自己监听到的事件类型触发回调函数。

    源码剖析

    channel.h

    ///
    ///一个能被选择的 I/O channel
    //这个类不拥有自己的文件描述符
    /// 这个文件描述符可能是socket,eventfd, timerfd,或者 signalfd.
    
    class Channel : noncopyable
    {
     public:
      typedef std::function<void()> EventCallback;	//事件回调
      typedef std::function<void(Timestamp)> ReadEventCallback;	//读事件回调
    
      Channel(EventLoop* loop, int fd);	//一个channel属于一个loop,一个loop可以对应多个channel
      ~Channel();
    
      void handleEvent(Timestamp 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与shared_ptr管理的所有者对象绑定,
      ///阻止shared_ptr管理的所有者对象在handleEvent被销毁
      void tie(const std::shared_ptr<void>&);
    
      int fd() const { return fd_; }
      int events() const { return events_; }	//返回注册的事件
      void set_revents(int revt) { revents_ = revt; } // 设置监听到的事件类型
      // int revents() const { return revents_; }
      bool isNoneEvent() const { return events_ == kNoneEvent; }
    
      void enableReading() { events_ |= kReadEvent; update(); }	//监听读事件
      void disableReading() { events_ &= ~kReadEvent; update(); }	//取消监听读事件
      void enableWriting() { events_ |= kWriteEvent; update(); }	//监听写事件
      void disableWriting() { events_ &= ~kWriteEvent; update(); }	//取消监听写事件
      void disableAll() { events_ = kNoneEvent; update(); }	//不监听任何事件
      bool isWriting() const { return events_ & kWriteEvent; }	//是否监听写事件
      bool isReading() const { return events_ & kReadEvent; }	//是否监听读事件
    
      // for Poller
      int index() { return index_; }	//poller事件数组中的下标
      void set_index(int idx) { index_ = idx; }	
    
      // for debug
      string reventsToString() const;
      string eventsToString() const;
    
      void doNotLogHup() { logHup_ = false; }
    
      EventLoop* ownerLoop() { return loop_; }	//返回所属的loop
      void remove();	//将channel从loop中移除
    
     private:
      static string eventsToString(int fd, int ev);
    
      void update();	//根据index_的状态,控制监听数组
      void handleEventWithGuard(Timestamp receiveTime);	//对监听到事件的一个处理
    
      static const int kNoneEvent;  //无事件
      static const int kReadEvent;  //可读事件
      static const int kWriteEvent; //可写事件
    
      EventLoop* loop_;
      const int  fd_;
      int        events_;	//监听的事件类型
      int        revents_; 	//监听到的事件类型 
      int        index_; 	// 表示poller事件数组中的序号
      bool       logHup_;	
    
      std::weak_ptr<void> tie_;
      bool tied_;	
      bool eventHandling_;	//是否处于处理事件中
      bool addedToLoop_;	//是否添加到loop上
      ReadEventCallback readCallback_;	//读回调
      EventCallback writeCallback_;	//写回调
      EventCallback closeCallback_;	//关闭回调
      EventCallback errorCallback_;	//错误回调
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82

    channel.cc

    // Copyright 2010, Shuo Chen.  All rights reserved.
    // http://code.google.com/p/muduo/
    //
    // Use of this source code is governed by a BSD-style license
    // that can be found in the License file.
     
    // Author: Shuo Chen (chenshuo at chenshuo dot com)
     
    #include "muduo/base/Logging.h"
    #include "muduo/net/Channel.h"
    #include "muduo/net/EventLoop.h"
     
    #include 
     
    #include 
     
    using namespace muduo;
    using namespace muduo::net;
     
    const int Channel::kNoneEvent = 0;
    const int Channel::kReadEvent = POLLIN | POLLPRI;
    const int Channel::kWriteEvent = POLLOUT;
     
    //Channel(事件分发器),只属于一个EventLoop,Channel类中保存着IO事件的类型以及对应的回调函数,每个channel只负责一个文件描述符
    Channel::Channel(EventLoop* loop, int fd__)
      : loop_(loop),        //channel所属的loop,一个channel只属于一个loop
        fd_(fd__),          //channel负责的文件描述符
        events_(0),         //注册的事件
        revents_(0),        //poller设置的就绪的事件
        index_(-1),         //被poller使用的下标
        logHup_(true),      //是否生成某些日志
        tied_(false),   用于tie()方法
        eventHandling_(false),          //处理handevent的标志
        addedToLoop_(false)		//是否正在循环监听中
    {
    }
     
    Channel::~Channel()
    {
      assert(!eventHandling_);
      assert(!addedToLoop_);
      if (loop_->isInLoopThread())//one loop per thread,判断event_loop所在的线程和正在销毁该对象的线程是不是同一个线程
      {
        assert(!loop_->hasChannel(this));//该对象此时不属于loop_并且poller中也没有该记录才能销毁成功
      }
    }
     
    void Channel::tie(const std::shared_ptr<void>& obj)
    {
      tie_ = obj;
      tied_ = true;
    }
     
    void Channel::update()   //根据index_的状态,控制该channel的监听状态
    {
      addedToLoop_ = true;
      loop_->updateChannel(this);//该函数最底层实际是由Poller->updateChannel(this)实现的
    }
     
    void Channel::remove()  //同上
    {
      assert(isNoneEvent());
      addedToLoop_ = false;
      loop_->removeChannel(this);//将该对象从Poller监听的数组对象中移除出去
    }
     
    //处理所有发生的事件,如果活着,底层调用handleEventWithGuard
    void Channel::handleEvent(Timestamp receiveTime) //事件到来调用handleEvent处理
    {
      std::shared_ptr<void> guard;       //守护
      if (tied_)
      {
        guard = tie_.lock();
        if (guard)
        {
          handleEventWithGuard(receiveTime);//实际上处理的函数
        }
      }
      else
      {
        handleEventWithGuard(receiveTime);
      }
    }
     
    //处理所有发生的事件
    //EPOLLIN :表示对应的文件描述符可以读; 
    //EPOLLOUT:表示对应的文件描述符可以写; 
    //EPOLLPRI:表示对应的文件描述符有紧急的数据可读 
    //EPOLLERR:表示对应的文件描述符发生错误; 
    //EPOLLHUP:表示对应的文件描述符被挂断; 
    //EPOLLET:表示对应的文件描述符有事件发生; 
    void Channel::handleEventWithGuard(Timestamp receiveTime)
    {					
      eventHandling_ = true;			
      LOG_TRACE << reventsToString();
      if ((revents_ & POLLHUP) && !(revents_ & POLLIN))  //判断返回事件类型
      {
        if (logHup_)
        {
          LOG_WARN << "fd = " << fd_ << " Channel::handle_event() POLLHUP";
        }
        if (closeCallback_) closeCallback_();
      }
     
      if (revents_ & POLLNVAL)                 //不合法文件描述符
      {
        LOG_WARN << "fd = " << fd_ << " Channel::handle_event() POLLNVAL";
      }
     
      if (revents_ & (POLLERR | POLLNVAL))
      {
        if (errorCallback_) errorCallback_();
      }
      if (revents_ & (POLLIN | POLLPRI | POLLRDHUP))   //POLLRDHUP是对端关闭连接事件,如shutdown等
      {
        if (readCallback_) readCallback_(receiveTime);
      }
      if (revents_ & POLLOUT)
      {
        if (writeCallback_) writeCallback_();
      }
      //事件处理完成,将事件处理标志职位false
      eventHandling_ = false;
    }
     
    //将revents转为string,方便输出调试
    string Channel::reventsToString() const
    {
      return eventsToString(fd_, revents_);
    }
     
    //将events转为string,方便输出调试
    string Channel::eventsToString() const
    {
      return eventsToString(fd_, events_);
    }
     
    //实际上events和revents转为string类型的实现
    string Channel::eventsToString(int fd, int ev)
    {
      std::ostringstream oss;
      oss << fd << ": ";
      if (ev & POLLIN)
        oss << "IN ";
      if (ev & POLLPRI)
        oss << "PRI ";
      if (ev & POLLOUT)
        oss << "OUT ";
      if (ev & POLLHUP)
        oss << "HUP ";
      if (ev & POLLRDHUP)
        oss << "RDHUP ";
      if (ev & POLLERR)
        oss << "ERR ";
      if (ev & POLLNVAL)
        oss << "NVAL ";
     
      return oss.str();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
  • 相关阅读:
    第5章相似矩阵及二次型(4)
    现代计算与光学的跨界机遇——
    三个线程交替打印的几种实现方式
    【项目管理】PMO技能树21项参照
    自采集壁纸网页源码
    百炼成钢 —— 声网实时网络的自动运维丨Dev for Dev 专栏
    开发过程教学——交友小程序
    《SQL基础教程》
    鸿蒙手表开发之使用adb命令安装线上包
    【数据结构-栈 二】【单调栈】每日温度、接雨水
  • 原文地址:https://blog.csdn.net/weixin_50448879/article/details/133964879