muduo网络库之Channel类

本篇博客针对Channel类做下小结。

博客代码来自于陈硕的muduo网络库,github地址https://github.com/chenshuo/muduo

工作原理:

Channel对象自始至终负责一个文件描述符fd的IO事件分发,Channel对象通常被更上层的类拥有,比如TimerQueue,TimerQueue在构造期间会实例化一个定时器对象,timerfd_为该定时器fd,随后利用该定时器fd实例化一个Channel对象timerfdChannel_,设置该Channel的回调函数和events_后,Channel对象会被update到Poller的std::map<int, Channel*>映射表,这样每当Channel监听的事件得到响应时,EventLoop的loop循环会调用Channel相应的回调函数。而该Channel的回调函数和TimerQueue设置的用户回调函数通过std::bind绑定在一起,这样Channel就把事件通知和用户回调函数通过Poller::poll()连接起来了。

学习笔记:

在moduo网络库中通常将Channel对象和某个可读写的资源绑定在一起,用于实时监听该资源的读写情况。通过资源描述符fd和Channel对象注册到Poller的映射表中,将事件通知和用户回调函数连接了起来。Channel类不拥有fd,析构的时候也不需要关闭fd,Channel类会把不同的IO事件分发为不同的回调,例如ReadCallback、WriteCallback等,用户一般不会直接使用Channel类,而回使用更上层的类诸如TimerQueue、TcpConnection等。

Channel.h

#ifndef MUDUO_NET_CHANNEL_H
#define MUDUO_NET_CHANNEL_H

#include "muduo/base/noncopyable.h"
#include "muduo/base/Timestamp.h"

#include <functional>
#include <memory>

namespace muduo
{
namespace net
{

class EventLoop;

///
/// 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();

  //设置回调,一般是把该Channel拥有者的回调相应绑定到Channenl中
  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.
  /*Channel通常作为其它类的成员,比如TcpConnection,而Channel的回调函数通常和TcpConnection
  通过std::bind绑定,当Poller通知该Channel的回调时,Channel会调用TcpConnection对应的回调,
  而此时TcpConnection的生命周期尚未可知,此时tie_保存TcpConnection的this指针,通过将tie_
  的weak_ptr提升为shared_ptr成功与否判断TcpConnection是否健在。*/
  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; }

  //update()会将修改后的events_写入到Poller中,修改当前Channel的监听状态
  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_; }
  //remove移除Poller映射表保存的Channel地址,避免悬垂指针
  void remove();

 private:
  static string eventsToString(int fd, int ev);
  //更新Channel在Poller中的监听状态(增、删、改)
  void update();
  //事件回调
  void handleEventWithGuard(Timestamp receiveTime);

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

  EventLoop* loop_;    //Channel所属的loop_
  const int  fd_;      //Channel和fd_打包注册到Poller的映射表
  int        events_;  //设置需要监听的事件类型(读、写、错误)
  int        revents_; // it's the received event types of epoll or poll
  int        index_;   // used by Poller.
  bool       logHup_;  //是否打印挂起日志

  std::weak_ptr<void> tie_; //绑定该Channel拥有者的this指针
  bool tied_;               //是否已把tie_和其他类this指针绑定
  bool eventHandling_;      //是否正在处理回调
  bool addedToLoop_;        //是否注册到Poller中监听
  ReadEventCallback readCallback_;
  EventCallback writeCallback_;
  EventCallback closeCallback_;
  EventCallback errorCallback_;
};

}  // namespace net
}  // namespace muduo

#endif  // MUDUO_NET_CHANNEL_H

Channel.cpp

#include "muduo/base/Logging.h"
#include "muduo/net/Channel.h"
#include "muduo/net/EventLoop.h"

#include <sstream>

#include <poll.h>

using namespace muduo;
using namespace muduo::net;

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

Channel::Channel(EventLoop* loop, int fd__)
  : loop_(loop),
    fd_(fd__),
    events_(0),
    revents_(0),
    index_(-1),
    logHup_(true),
    tied_(false),
    eventHandling_(false),
    addedToLoop_(false)
{
}

Channel::~Channel()
{
  assert(!eventHandling_);
  assert(!addedToLoop_);
  if (loop_->isInLoopThread())
  {
    assert(!loop_->hasChannel(this));
  }
}

void Channel::tie(const std::shared_ptr<void>& obj)
{
  /*tie_绑定Channel拥有者的this指针,tie_是weak_ptr类型,不会延长obj的生命周期,
  当tie_提升shared_ptr失败表示Channel拥有者已经销毁了
  */
  tie_ = obj;
  tied_ = true;
}

void Channel::update()
{
  //需要通过loop_到Poller中修改Channel的监听状态
  addedToLoop_ = true;
  loop_->updateChannel(this);
}

void Channel::remove()
{
  //需要通过loop_到Poller的映射表中删除Channel的指针
  assert(isNoneEvent());
  addedToLoop_ = false;
  loop_->removeChannel(this);
}

void Channel::handleEvent(Timestamp receiveTime)
{
  std::shared_ptr<void> guard;
  if (tied_)
  {
    guard = tie_.lock();
    if (guard)
    {
      //tie_提升shared_ptr成功,说明该Channel拥有者仍然健在
      handleEventWithGuard(receiveTime);
    }
    //该Channel拥有者销毁了,什么也不做...
  }
  else
  {
    //该Channel没有拥有者
    handleEventWithGuard(receiveTime);
  }
}

void Channel::handleEventWithGuard(Timestamp receiveTime)
{
  //解析从Poller::poll()返回的revents_,执行相应的回调
  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))
  {
    if (readCallback_) readCallback_(receiveTime);
  }
  if (revents_ & POLLOUT)
  {
    if (writeCallback_) writeCallback_();
  }
  eventHandling_ = false;
}

string Channel::reventsToString() const
{
  return eventsToString(fd_, revents_);
}

string Channel::eventsToString() const
{
  return eventsToString(fd_, events_);
}

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

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值