引言
- 必须将产生事件的文件描述符封装在Channel。这里的文件描述符可以是file descriptor,可以是socket,还可以是timefd,signalfd。
- 文件描述符是最终需要加入到linux的epoll或者select等IO复用设施上的
Channel
简介
Channel相当于libevent中的事件event
Channel是Reactor结构中的“事件”,它自始至终都只属于一个EventLoop,每个Channel对象自始至终只负责一个fd的IO事件分发。
==> 可以将Channel安插到EventLoop上,用EventLoop监视该Channel,当EventLoop监视到Channel关注的事件发生时,就会回调Channel的事件回调函数。
Channel
的使用步骤
Channel的使用步骤 | |
---|---|
构造函数 | 构造Channel对象:使用fd创建Channel对象,并给Channel关联EventLoop |
set___CallBack | 给Channel对象注册Read/Write回调函数 |
enable*、disable* | 调用enable*、disable*给Channel对象关注和取消读写事件 |
loop() | 调用EventLoop的poll/epoll循环监控Channel对象可读/可写事件是否被触发,一旦触发,立即执行注册的回调函数 |
源码剖析
根据[Channel的使用步骤]对源码进行剖析
1. 构造函数
Channel::Channel(EventLoop* loop, int fd__)
: loop_(loop), //该Channel所属的loop
fd_(fd__), //关联的文件描述符fd
events_(0), //关注的事件
revents_(0), //epoll/poll返回的被激活的事件
index_(-1),
logHup_(true),
tied_(false),
eventHandling_(false), //是否处于事件处理中
addedToLoop_(false)
{
}
2. set___CallBack
:给Channel对象注册Read/Write回调函数
(1) 回调函数的类型分为两大类
//事件回调处理
typedef std::function<void()> EventCallback;
//读事件的回调处理,传一个时间戳
typedef std::function<void(Timestamp)> ReadEventCallback;
(2) 成员函数
private:
ReadEventCallback readCallback_;
EventCallback writeCallback_;
EventCallback closeCallback_;
EventCallback errorCallback_;
(3) 设置回调函数set___Callback
设置回调函数,实际上是将用户自定义的回调函数,给成员函数赋值
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); }
3. enable\*、disable\*
: 调用enable*、disable*给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(); }
void Channel::update() //更新事件类型
{
addedToLoop_ = true;
loop_->updateChannel(this);
}
执行时序图
(1) 当Channel调用enable*、disable*,修改了关注的事件events_后,将调用update()函数
(2) update函数,调用了loop_->updateChannel(this),将该Channel从Poller::channels_删除或添加到Poller::channels_中
4. 调用loop->loop()
监控事件的发生,当关注的事件发生后,会回调通过set___Callback注册的回调函数
解释:由前面的博文muduo_net代码剖析之EventLoop
void EventLoop::loop()
{
... ...
while (!quit_)
{
... ...
for (Channel* channel : activeChannels_)
{
currentActiveChannel_ = channel;
//激活的Channel,调用handleEvent
//handleEvent又去回调通过set___Callback设置的回调函数
currentActiveChannel_->handleEvent(pollReturnTime_);
}
... ...
}
... ...
}
void Channel::handleEvent(Timestamp receiveTime)
{
std::shared_ptr<void> guard;
if (tied_)
{
guard = tie_.lock();
if (guard)
{
handleEventWithGuard(receiveTime);
}
}
else
{
handleEventWithGuard(receiveTime);
}
}
//事件发生后,根据事件类型来调用之前使用set___Callback注册的回调函数
void Channel::handleEventWithGuard(Timestamp receiveTime)
{
eventHandling_ = true;
LOG_TRACE << reventsToString();
if ((revents_ & POLLHUP) && !(revents_ & POLLIN))
{
if (logHup_)//如果有POLLHUP事件,输出警告信息
{
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_();
}
//POLLIN:listsock有新的连接到来、sockfd可读
//POLLRDHUP:对端关闭连接事件,如shutdown等
if (revents_ & (POLLIN | POLLPRI | POLLRDHUP))
{
if (readCallback_) readCallback_(receiveTime);
}
//outputBuffer_中有数据,导致sockfd可写
// 将outputBuffer_中的数据写入到sockfd(即内核中的缓冲区)中
if (revents_ & POLLOUT)
{
if (writeCallback_) writeCallback_();
}
eventHandling_ = false;//处理完了=false
}
... ...
示例代码
timerfd_create 生成一个定时器对象,返回文件描述符
timerfd_settime 能够启动和停止定时器