1. 程式人生 > >muduo_net程式碼剖析之Channel

muduo_net程式碼剖析之Channel

Channel簡介

  1. Channel是Reactor結構中的“事件”,它自始至終都屬於一個EventLoop,相當於libevent中的event_base,可以將event_base安插到EventLoop上,用EventLoop監視該Channel
  2. Channel負責一個檔案描述符的IO事件,它包含檔案描述符fd_,但實際上它不擁有fd_,不用負責將其關閉。
  3. 在Channel類中儲存這IO事件的型別以及對應的回撥函式,當IO事件發生時,最終會呼叫到Channel類中的回撥函式。Channel類一般不單獨使用,它常常包含在其他類中(Acceptor、Connector、EventLoop、TimerQueue、TcpConnection)使用。

Channel的使用步驟

在這裡插入圖片描述

Channel的使用步驟
構造Channel物件:使用fd建立Channel物件,並給Channel關聯EventLoop
給Channel物件註冊Read/Write回撥函式
呼叫enable*、disable*給Channel物件關注和取消讀寫事件
呼叫EventLoop的poll/epoll迴圈監控Channel物件可讀/可寫事件是否被觸發,一旦觸發,立即執行註冊的回撥函式
  1. Channel類有EventLoop的指標 loop_,通過這個指標可以向EventLoop中添加當前Channel事件。事件型別用events_表示,不同事件型別對應不同回撥函式
ReadEventCallback readCallback_;
EventCallback writeCallback_;
EventCallback closeCallback_;
EventCallback errorCallback_;
  1. 呼叫enable*、disable*關注和取消讀寫事件,都呼叫了update()函式,update()函式更新關心事件的時序圖:

    在這裡插入圖片描述

Channel原始碼分析

Channel.h

class EventLoop;

class Channel : noncopyable
{
 public:
  typedef std::function<void()> EventCallback;//事件回撥處理
  typedef std::function<void(Timestamp)> ReadEventCallback;//讀事件的回撥處理,傳一個時間戳

  Channel(EventLoop* loop, int fd);//建構函式
  ~Channel();

  //在EventLoop::loop()中呼叫了handleEvent()函式,用於處理被啟用的事件
  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); }

... ...

  //新增/刪除修改的事件,呼叫了update()函式
  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_; }
  void set_index(int idx) { index_ = idx; }

  // for debug
  string reventsToString() const;
  string eventsToString() const;

  void doNotLogHup() { logHup_ = false; }

  EventLoop* ownerLoop() { return loop_; }
  void remove();

 private:
... ...

  static const int kNoneEvent;
  static const int kReadEvent;
  static const int kWriteEvent;

  EventLoop* loop_; //所屬EventLoop
  const int  fd_; //檔案描述符,但是不負責關閉該fd
  int        events_; //關注的事件
  int        revents_; //epoll/poll返回的事件
  int        index_; // used by Poller.表示在poll事件陣列中的序號
  bool       logHup_;// for POLLHUP

  std::weak_ptr<void> tie_;
  bool tied_;
  bool eventHandling_; //是否處於處理事件中
  bool addedToLoop_;
  
... ...
};

Channel.cc

const int Channel::kNoneEvent = 0;
const int Channel::kReadEvent = POLLIN | POLLPRI;
const int Channel::kWriteEvent = POLLOUT;

... ... 
void Channel::update() //更新事件型別
{
  addedToLoop_ = true;
  loop_->updateChannel(this); //呼叫loop的,loop再呼叫poll的註冊pollfd
}

... ...
void Channel::handleEvent(Timestamp receiveTime)
{
  std::shared_ptr<void> guard;
  if (tied_)
  {
    guard = tie_.lock();
    if (guard)
    {
      handleEventWithGuard(receiveTime);
    }
  }
  else
  {
    handleEventWithGuard(receiveTime);
  }
}
//事件發生後,根據事件型別來呼叫回撥函式
void Channel::handleEventWithGuard(Timestamp receiveTime)
{
  eventHandling_ = true;
  LOG_TRACE << reventsToString();
  if ((revents_ & POLLHUP) && !(revents_ & POLLIN)) //判斷返回事件型別
  {
    if (logHup_)//如果有POLLHUP事件,輸出警告資訊
    {
      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_();
  }
  eventHandling_ = false;//處理完了=false
}
... ...

handleEvent()函式的使用示例

示例程式碼1

在EventLoop類的成員函式EventLoop::loop()中,呼叫了handleEvent(),用於處理啟用的事件

//事件迴圈,該函式不能跨執行緒呼叫,只能在建立該物件的執行緒中呼叫
void EventLoop::loop()
{
... ...
  while (!quit_)
  {
    ... ...
    //遍歷每一個啟用的通道currentActiveChannel_,並handleEvent
    for (Channel* channel : activeChannels_)
    {
      currentActiveChannel_ = channel;
      currentActiveChannel_->handleEvent(pollReturnTime_);
    }
    ... ...
  }
... ...
}

示例程式碼2

在這裡插入圖片描述