木铎库源码剖析—Channel

Channel

Channel类概述

​ Channel类其实相当于一个文件描述符的保姆;在TCP网络编程中,想要IO多路复用监听某个文件描述符,就要把这个fd和该fd感兴趣的事件通过epoll_ctl注册到IO多路复用模块(我管它叫事件监听器)上。当事件监听器监听到该fd发生了某个事件。事件监听器返回 [发生事件的fd集合]以及[每个fd都发生了什么事件]

​ Channel类则封装了一个[fd]和这个[fd感兴趣事件]以及事件监听器监听到[该fd实际发生的事件]。同时channel类还提供了设置该fd的感兴趣事件,以及将该fd及感兴趣事件注册到事件监听器或从事件监听器中移除,以及保持了该fd的每种事件对应的处理函数。

Channel类的成员变量

EventLoop* loop_;    //指向所属的EventLoop对象
const int  fd_;      //管理的文件描述符
int        events_;  //当前注册的事件类型
int        revents_; // it's the received event types of epoll or poll 返回正在发生的事件类型
int        index_;   // used by Poller.  在事件循环中的位置
bool       logHup_;  //标识是否记录挂起事件

这些都是相应的回调函数

ReadEventCallback readCallback_;
EventCallback     writeCallback_;
EventCallback     closeCallback_;
EventCallback     errorCallback_;

channel类重要的成员方法

向Channel对象注册各类事件的处理函数

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

一个文件描述符会发生可读、可写、关闭、错误事件。当发生这些事件后,就需要调用相应的处理函数来处理。外部通过调用上面这四个函数可以将事件处理函数放进Channel类中,当需要调用的时候就可以直接拿出来调用了。

访问及修改各类事件成员的成员方法

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

//如果设置为true 表示在日志中不记录HUP事件
void doNotLogHup() { logHup_ = false; }
//返回Channel所属的EventLoop对象
EventLoop* ownerLoop() { return loop_; }

Channel中给出了两个DEBUG阶段可以调用的成员函数

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

​ handleEvent为相对重要的成员函数,它是一个分发事件处理函数,用于区分fd到底发生了什么事件,然后执行对用的回调函数,可以看出具体执行都是由handleEventWithGuard函数执行的;而为什么要判断绑定的对象是否存在呢,可以发现该成员变量其实是一个weak_ptr变量,弱引用,并不会真假shared_ptr的引用计数,通过这种弱绑定,并在此处调用时进行赋值主要是防止对象在事件处理期间被销毁。

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_)
    {
      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;
}

下面这两个函数用于对Channel的更新和移除

void Channel::update()
{
  addedToLoop_ = true;
  loop_->updateChannel(this);
}

void Channel::remove()
{
  assert(isNoneEvent());
  addedToLoop_ = false;
  loop_->removeChannel(this);
}
posted @ 2024-02-28 20:39  孟秋十三  阅读(11)  评论(0)    收藏  举报