木铎库源码剖析—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);
}

浙公网安备 33010602011771号