muduo网络库(3):对套接字和监听事件的封装Channel

一、概述

Channel类中数据成员:
  • 描述符fd;
  • 需要监听的事件events;
  • fd被激活调用的可读/可写/关闭/错误回调函数;

`

 const int  fd_;                    //描述符fd;
 int  events_;                      //需要监听的事件events;
 EventCallback readCallback_;       //fd被激活调用的可读回调函数;
 EventCallback writeCallback_;      //fd被激活调用的可写回调函数;
 EventCallback closeCallback_;      //fd被激活调用的关闭回调函数;
 EventCallback errorCallback_;      //fd被激活调用的错误回调函数;

Poller类监听的其实就是一个个Channel对象,Channel可以向Poller注册自己关心的事件,当被激活后调用相应的回调函数。类似libevent的struct event。

二、Channel在整个事件驱动循环中的大致流程

  1. Acceptor接收到客户端请求,调用TcpServer回调函数
  2. TcpServer回调函数中创建TcpConnection对象,代表着一个Tcp连接
  3. TcpConnection构造函数中创建Channel对象,保存客户端套接字fd关心的事件(可读);
  4. Channel注册自己到所属事件驱动循环(EventLoop) 中的 Poller上;
  5. Poller开始监听,当发现Channel激活后将其添加到EventLoop激活队列中;
  6. EventLooppoll返回后处理激活队列中的Channel,调用其处理函数
  7. Channel处理函数中根据被激活的原因调用不同的回调函数(可读/可写等)。

其中,各个类分别是:

  1. Acceptor监听类,用于监听客户端请求,然后接收客户端;
  2. TcpServer服务器类,用于管理所有的TcpConnection;
  3. TcpConnectionTcp连接类,代表一个Tcp连接,内部保存对应的Channel;
  4. Channel,套接字和相应事件及回调函数的封装,一个事件类;
  5. Poller,io复用的封装,用于监听Channel
  6. EventLoop,事件驱动主循环,调用poll函数,管理激活队列,类似libevent的struct event_base
    在这里插入图片描述

三、函数对象std::function

C++基于对象其实就是利用std::function/std::bind实现的,作用是绑定某个类的函数指针 。使用方法如:

#include <functional>
typedef std::function<void()> Callback;

void function_bind_test(Callback func)
{
    func();/* 这里调用相当于conn->connectionEstablished() */
}

function_bind_test(std::bind(&TcpConnection::connectionEstablished, conn));

std::function/std::bind为类的成员函数的调用提供更多灵活性,在C++11引入lambda后逐渐取代了std::bind,C++14后lambda支持模板参数后完全取代了std::bind

四、Channel定义如下,成员函数主要就是

  1. 设置回调函数set*Callback
  2. 设置对fd关心的事件,将自己存储的fd及相应的事件注册到Poller中enable*
  3. 删除对fd的监听,将其从PollerChannelMap中移除disableAll
  4. 被激活时调用的回调函数hanleEvent
/* 
 * Channel其实就是一个事件类,保存fd和需要监听的事件,以及各种回调函数
 * 类似libevent的struct event
 */
class Channel : noncopyable
{
 public:

     /* 
      * function,可调用函数对象,类似函数指针
      * 模板参数是函数标签,例如
      *     function<int(int, int)> func;
      *     这个函数对象的两个参数类型都是int,返回值类型是int
      */
  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.
  /* 用于保存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; }

  /* 将自己注册到Poller中,或从Poller中移除,或只删除某个事件,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 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_;
  const int  fd_;
  int        events_;
  int        revents_; // it's the received event types of epoll or poll
  /* 
   * 保存fd在epoll/poll中的状态,有:
   *    还没有添加到epoll中
   *    已经添加到epoll中
   *    添加到epoll中,又从epoll中删除了
   */
  int        index_; // used by Poller.
  bool       logHup_;

  /* 
   * tie_存储的是TcpConnection类型的指针,即TcpConnectionPtr
   * 一个TcpConnection代表一个已经建立好的Tcp连接
   */
  std::weak_ptr<void> tie_;
  bool tied_;
  bool eventHandling_;
  bool addedToLoop_;
  ReadEventCallback readCallback_;
  EventCallback writeCallback_;
  EventCallback closeCallback_;
  EventCallback errorCallback_;
};

五、.cpp中的几个比较重要的函数介绍

/* 
 * 由TcpConnection中connectEstablished函数调用
 * 作用是当建立起一个Tcp连接后,让用于监测fd的Channel保存这个Tcp连接的弱引用
 * tie_是weak_ptr的原因
 *      weak_ptr是弱引用,不增加引用基数,当需要调用内部指针时
 *      可通过lock函数提升成一个shared_ptr,如果内部的指针已经销毁
 *      那么提升的shared_ptr是null
 *      可以通过是否是null判断Tcp是否还处于连接,因为如果断开,那么这个TcpConnection就被销毁了
 */
void Channel::tie(const std::shared_ptr<void>& obj)
{
  tie_ = obj;
  tied_ = true;
}

因为Channel的回调函数其实调用的是TcpConnection中的函数,所以如果想要调用,就必须知道TcpConnection的指针,而如果TcpConnection被销毁了,那么指针调用会出错,所以采用智能指针,又因为Channel只需要知道TcpConnection是否还存在,所以不需要使用shared_ptr,这会增加引用计数,导致TcpConnection销毁不了(如果close连接,TcpConnection本应该被销毁,但是因为Channel中也留有对TcpConnection的引用,所以不会销毁,糟糕的事情…),所以采用weak_ptr,不增加引用计数,适当时可以提升为shared_ptr

/*
 * 根据fd激活事件的不同,调用不同的fd的回调函数
 */
void Channel::handleEvent(Timestamp receiveTime)
{
  /* 
   * RAII,对象管理资源
   * weak_ptr使用lock提升成shared_ptr,此时引用计数加一
   * 函数返回,栈空间对象销毁,提升的shared_ptr guard销毁,引用计数减一
   */
  std::shared_ptr<void> guard;
  if (tied_)
  {
    guard = tie_.lock();
    if (guard)
    {
      handleEventWithGuard(receiveTime);
    }
  }
  else
  {
    handleEventWithGuard(receiveTime);
  }
}

经典的RAII应用场景,配合智能指针
如果想要判断Channel所在的TcpConnection是否仍然存在(没有被关闭),可以直接将weak_ptr提升为shared_ptr,如果提升成功(不为NULL),代表TcpConnection存在,调用回调函数即可。
提升成shared_ptrTcpConnection引用计数变为2,handleEvent函数返回后局部变量guard销毁,引用计数恢复到1。
同样的应用还有锁对象,创建时上锁,析构时解锁。

handleEventWithGuard其实就是根据不同的激活原因滴啊用不同的回调函数,这些回调函数都在TcpConnection中,也是通过上面std::function/std::bind设置的,所以在调用前必须判断Channel所在的TcpConnection是否还存在

/*
 * 根据被激活事件的不同,调用不同的回调函数
 */
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))
  {
    if (readCallback_) readCallback_(receiveTime);
  }
  if (revents_ & POLLOUT)
  {
    if (writeCallback_) writeCallback_();
  }
  eventHandling_ = false;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值