C/C++教程

muduo源码分析之Cannel通道

本文主要是介绍muduo源码分析之Cannel通道,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

相关文件

muduo/net/Channel.h
muduo/net/Channel.cc

作用

Channel可理解为一个文件描述符fd和如何处理它的事件的回调函数的封装。
Channel负责注册和响应IO事件。
muduo用户一般不直接使用Channel,而使用更上一层的封装,如TCPConnetion。

使用

这里是一个给出的测试示例

#include <muduo/net/Channel.h>
#include <muduo/net/EventLoop.h>

#include <boost/bind.hpp>

#include <stdio.h>
#include <sys/timerfd.h>

using namespace muduo;
using namespace muduo::net;

EventLoop* g_loop;
int timerfd;

void timeout()
{
  printf("Timeout!\n");
  //uint64_t howmany;
  ::read(timerfd, &howmany, sizeof howmany);
  g_loop->quit(); //退出
}

int main(void)
{
  EventLoop loop;
  g_loop = &loop;
  
  //使用timerfd实现一个单次触发的定时器
  timerfd = ::timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
  Channel channel(&loop, timerfd); //该channel的fd即定时器的fd
  channel.setReadCallback(timeout);//设置读事件的回调函数
  channel.enableReading();

  struct itimerspec howlong;
  bzero(&howlong, sizeof howlong);
  howlong.it_value.tv_sec = 5; //设置定时时间5s,定时时间到则触发事件
  ::timerfd_settime(timerfd, 0, &howlong, NULL);

  loop.loop(); //开启loop循环,监听事件

  ::close(timerfd);
}

Channel源码分析

Channel类

成员函数与变量小窥,主要是文件描述符和事件的回调函数。
一个Channel负责一个文件描述符fd的IO事件分发。
这里说到Channel不管理fd的生命期,即析构时不会关闭fd。

/// A selectable I/O channel.
///
/// This class doesn't own the file descriptor.
/// The file descriptor could be a socket,
/// an eventfd, a timerfd, or a signalfd
class Channel : noncopyable
{
 public:
  typedef std::function<void()> EventCallback;
  typedef std::function<void(Timestamp)> ReadEventCallback;

  Channel(EventLoop* loop, int fd);
  ~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); }

  /// Tie this channel to the owner object managed by shared_ptr,
  /// prevent the owner object being destroyed in 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; } // used by pollers
  // 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_; }
  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 string eventsToString(int fd, int ev);

  void update();
  void handleEventWithGuard(Timestamp receiveTime);

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

  EventLoop* loop_;   //每个Channel只属于一个Eventloop
  const int  fd_;     //所管理的文件描述符
  int        events_;  //监听的事件
  int        revents_; // poller返回的事件
  int        index_; // used by Poller.Poller管理多个Channel,用index标识该channel
  bool       logHup_;

  std::weak_ptr<void> tie_;
  bool tied_;
  bool eventHandling_;
  bool addedToLoop_;
  ReadEventCallback readCallback_;
  EventCallback writeCallback_;
  EventCallback closeCallback_;
  EventCallback errorCallback_;
};

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

事件处理

Channel::handleEvent()是Channel的核心,功能是根据poller返回的事件revents的值分别调用不同的用户回调函数。handleEvents()由EventLoop::loop()调用。

void Channel::handleEvent(Timestamp receiveTime)
{
  std::shared_ptr<void> guard;
  if (tied_)
  {
    guard = tie_.lock();
    if (guard)
    {
      handleEventWithGuard(receiveTime);
    }
  }
  else
  {
   //上面暂时不用管,直接看作调用了handleEventWithGuard()
    handleEventWithGuard(receiveTime);
  }
}

void Channel::handleEventWithGuard(Timestamp receiveTime)
{
  eventHandling_ = true;
  LOG_TRACE << reventsToString();
  
  //revents即poll/epoll返回的活跃事件
  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))
  {
    if (readCallback_) readCallback_(receiveTime); //读事件
  }
  if (revents_ & POLLOUT)
  {
    if (writeCallback_) writeCallback_();  //写事件
  }
  eventHandling_ = false;
}
这篇关于muduo源码分析之Cannel通道的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!