muduo网络库学习(二)对套接字和监听事件的封装Channel

来源:互联网 发布:淘宝客服介入会怎么样 编辑:程序博客网 时间:2024/05/22 02:07

muduo对描述符fd,需要监听的事件events,当fd被激活调用的可读/可写/关闭/错误回调函数进行了封装,实现在Channel类中,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. EventLoop在poll返回后处理激活队列中的Channel,调用其处理函数
  7. Channel在处理函数中根据被激活的原因调用不同的回调函数(可读/可写等)

其中

  1. Acceptor,监听类,用于监听客户端请求,然后接收客户端
  2. TcpServer,服务器类,用于管理所有的TcpConnection
  3. TcpConnection,Tcp连接类,代表一个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 0