时序图:
Channel类
- muduo中通过Channel对fd进行封装,fd可以是file descriptor,可以是socket,还可以是timefd,signalfd。其实更合适的说法是对fd事件相关方法的封装,例如负责注册fd的可读或可写事件到EvenLoop,又如fd产生事件后要如何响应。
- 一个fd对应一个channel, 它们是聚合关系,Channel在析构函数中并不会close掉这个fd(如socket是通过Socket类的对象离开临界区时close的,即RAII技法)。
- 它有一个handleEvent方法,当该fd有事件产生时EvenLoop会调用handleEvent方法进行处理,在handleEvent内部根据可读或可写事件调用不同的回调函数(回调函数可事先注册)。
- Channel类一般都不是单独使用的,通常都是用作其他类的成员,比如EvenLoop、TcpConnection、Acceptor、Connector这几个类。例如EvenLoop通过一个vector<Channel*> 对注册到其内的众多fd的管理,毕竟有了Channel就有了fd及其对应的事件处理方法,所以你会看到上图中EvenLoop与Channel是一对多的关系。
- Channel对象读写、更新都在一个I/O线程,读写不需要加锁。
数据成员:
static const int kNoneEvent:常量,表示没有事件
static const int kReadEvent:常量,表示读事件
static const int kWriteEvent:常量,表示写事件
EventLoop* loop_:Channel所属的EventLoop
const int fd_:Channel所负责的文件描述符,但不负责关闭该文件描述符
int events_:关注的事件
int revents_:poll/epoll返回的事件
int index_:如果使用的是PollPoller,表示该通道关注的事件在poll的事件数组中的序号;如果使用的是EPollPoll,表示通道的状态
bool logHup_:for POLLHUP
boost::weak_ptr tie_
bool tied_
bool eventHandling_:当前是否处于事件处理的状态
ReadEventCallback readCallback_:读回调函数
EventCallback writeCallback_:写回调函数
EventCallback closeCallback_:关闭回调函数
EventCallback errorCallback_:错误回调函数
typedef
typedef boost::function<void()> EventCallback:EventCallback是事件的回调处理
typedef boost::function<void(Timestamp)> ReadEventCallback:ReadEventCallback是读事件的回调处理
成员函数:
Channel(EventLoop* loop, int fd):构造函数,Channel记录Channel所属的EventLoop对象
~Channel():析构函数
void handleEvent(Timestamp receiveTime):当事件到来时,会调用handleEvent()进行处理
void setReadCallback(const ReadEventCallback& cb):读回调函数的注册,负责对所发生的I/O事件进行处理
void setWriteCallback(const EventCallback& cb):写回调函数的注册,负责对所发生的I/O事件进行处理
void setCloseCallback(const EventCallback& cb):关闭回调函数的注册,负责对所发生的I/O事件进行处理
void setErrorCallback(const EventCallback& cb):错误回调函数的注册,负责对所发生的I/O事件进行处理
void tie(const boost::shared_ptr&);
int fd() const :返回Channel所对应的文件描述符
int events() const :返回Channel关注的事件
void set_revents(int revt) :设置poll/epoll返回的事件
int revents() const :返回poll/epoll返回的事件的数目
bool isNoneEvent() const :判断是否没有事件发生,即判断events_是否等于kNoneEvent
void enableReading() :通道关注可读事件,调用update()把这个通道注册到EventLoop所持有的poller_对象中
void disableReading() :通道不关注可读事件,调用update()把这个通道注册到EventLoop所持有的poller_对象中
void enableWriting() :通道关注可写事件,调用update()把这个通道注册到EventLoop所持有的poller_对象中
void disableWriting() :通道不关注可写事件,调用update()把这个通道注册到EventLoop所持有的poller_对象中
void disableAll() :通道不关注所有事件,调用update()把这个通道注册到EventLoop所持有的poller_对象中
bool isWriting() const :判断当前通道是否关注写事件
int index():返回index
void set_index(int idx) :令poll的事件数组中的序号index_=idx
string reventsToString() const:把事件转换成字符串,以便调试
void doNotLogHup() :返回logHup_
EventLoop* ownerLoop():返回Channel所属的EventLoop,即loop_
void remove():负责移除I/O的可读或可写等事件
void update():负责更注册或者更新I/O的可读或可写等事件
void handleEventWithGuard(Timestamp receiveTime):handleEvent()会调用handleEventWithGuard()对事件进行处理
Channel.h
#ifndef MUDUO_NET_CHANNEL_H
#define MUDUO_NET_CHANNEL_H
#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <muduo/base/Timestamp.h>
namespace muduo
{
namespace net
{
class EventLoop;
///
/// A selectable I/O channel.
///
/// This class doesn't own the file descriptor.
/// The file descriptor could be a socket,
/// an eventfd, a timerfd, or a signalfd
class Channel : boost::noncopyable
{
public:
//EventCallback是事件的回调处理
typedef boost::function<void()> EventCallback;
//ReadEventCallback是读事件的回调处理
typedef boost::function<void(Timestamp)> ReadEventCallback;
//一个Channel对象只能属于一个EventLoop对象
//一个EventLoop对象可以拥有多个Channel对象
//Channel构造函数记录Channel所属的EventLoop对象
Channel(EventLoop* loop, int fd);
~Channel();
//当事件到来时,会调用handleEvent()进行处理
void handleEvent(Timestamp receiveTime);
//回调函数的注册,负责对所发生的I/O事件进行处理
void setReadCallback(const ReadEventCallback& cb)
{ readCallback_ = cb; }
void setWriteCallback(const EventCallback& cb)
{ writeCallback_ = cb; }
void setCloseCallback(const EventCallback& cb)
{ closeCallback_ = cb; }
void setErrorCallback(const EventCallback& cb)
{ errorCallback_ = cb; }
/// Tie this channel to the owner object managed by shared_ptr,
/// prevent the owner object being destroyed in handleEvent.
void tie(const boost::shared_ptr<void>&);
//返回Channel所对应的文件描述符
int fd() const { return fd_; }
//返回Channel注册的事件的数目events_
int events() const { return events_; }
//设置poll/epoll返回的事件的数目
void set_revents(int revt) { revents_ = revt; } // used by pollers
//返回poll/epoll返回的事件的数目
//int revents() const { return revents_; }
//判断是否没有事件发生,即判断events_是否等于kNoneEvent
bool isNoneEvent() const { return events_ == kNoneEvent; }
//通道关注可读事件,调用update()把这个通道注册到EventLoop所持有的poller_对象中
void enableReading() { events_ |= kReadEvent; update(); }
//通道不关注可读事件,调用update()把这个通道注册到EventLoop所持有的poller_对象中
//void disableReading() { events_ &= ~kReadEvent; update(); }
//通道关注可写事件,调用update()把这个通道注册到EventLoop所持有的poller_对象中
void enableWriting() { events_ |= kWriteEvent; update(); }
//通道不关注可写事件,调用update()把这个通道注册到EventLoop所持有的poller_对象中
void disableWriting() { events_ &= ~kWriteEvent; update(); }
//通道不关注所有事件,调用update()把这个通道注册到EventLoop所持有的poller_对象中
void disableAll() { events_ = kNoneEvent; update(); }
//判断当前通道是否关注写事件
bool isWriting() const { return events_ & kWriteEvent; }
// for Poller
//返回index_
int index() { return index_; }
//令poll的事件数组中的序号index_=idx
void set_index(int idx) { index_ = idx; }
// for debug
//把事件转换成字符串,以便调试
string reventsToString() const;
//返回logHup_
void doNotLogHup() { logHup_ = false; }
//返回Channel所属的EventLoop,即loop_
EventLoop* ownerLoop() { return loop_; }
//负责移除I/O的可读或可写等事件
void remove();
private:
//负责更注册或者更新I/O的可读或可写等事件
void update();
//handleEvent()会调用handleEventWithGuard()对事件进行处理
void handleEventWithGuard(Timestamp receiveTime);
//常量,表示没有事件
static const int kNoneEvent;
//常量,表示读事件
static const int kReadEvent;
//常量,表示写事件
static const int kWriteEvent;
//Channel所属的EventLoop
EventLoop* loop_;
//文件描述符,但不负责关闭该文件描述符
const int fd_;
//关注的事件
int events_;
//poll/epoll返回的事件
int revents_;
//如果使用的是PollPoller,表示该通道关注的事件在poll的事件数组中的序号
//如果使用的是EPollPoll,表示通道的状态
int index_;
//for POLLHUP
bool logHup_;
boost::weak_ptr<void> tie_;
bool tied_;
//当前是否处于事件处理的状态
bool eventHandling_;
//四个回调函数
ReadEventCallback readCallback_;
EventCallback writeCallback_;
EventCallback closeCallback_;
EventCallback errorCallback_;
};
}
}
#endif // MUDUO_NET_CHANNEL_H
Channel.cc
#include <muduo/base/Logging.h>
#include <muduo/net/Channel.h>
#include <muduo/net/EventLoop.h>
#include <sstream>
#include <poll.h>
using namespace muduo;
using namespace muduo::net;
//设置三个常量的初始值
const int Channel::kNoneEvent = 0;
const int Channel::kReadEvent = POLLIN | POLLPRI;
const int Channel::kWriteEvent = POLLOUT;
Channel::Channel(EventLoop* loop, int fd__)
: loop_(loop),
fd_(fd__),
events_(0),
revents_(0),
index_(-1),
logHup_(true),
tied_(false),
eventHandling_(false)
{
}
Channel::~Channel()
{
assert(!eventHandling_);
}
void Channel::tie(const boost::shared_ptr<void>& obj)
{
tie_ = obj;
tied_ = true;
}
void Channel::update()
{
loop_->updateChannel(this);
}
// 调用这个函数之前确保调用disableAll
void Channel::remove()
{
assert(isNoneEvent());
loop_->removeChannel(this);
}
void Channel::handleEvent(Timestamp receiveTime)
{
boost::shared_ptr<void> guard;
if (tied_)
{
guard = tie_.lock();
if (guard)
{
handleEventWithGuard(receiveTime);
}
}
else
{
handleEventWithGuard(receiveTime);
}
}
void Channel::handleEventWithGuard(Timestamp receiveTime)
{
eventHandling_ = true;
if ((revents_ & POLLHUP) && !(revents_ & POLLIN))
{
if (logHup_)
{
LOG_WARN << "Channel::handle_event() POLLHUP";
}
if (closeCallback_) closeCallback_();
}
if (revents_ & POLLNVAL)
{
LOG_WARN << "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;
}
string Channel::reventsToString() const
{
std::ostringstream oss;
oss << fd_ << ": ";
if (revents_ & POLLIN)
oss << "IN ";
if (revents_ & POLLPRI)
oss << "PRI ";
if (revents_ & POLLOUT)
oss << "OUT ";
if (revents_ & POLLHUP)
oss << "HUP ";
if (revents_ & POLLRDHUP)
oss << "RDHUP ";
if (revents_ & POLLERR)
oss << "ERR ";
if (revents_ & POLLNVAL)
oss << "NVAL ";
return oss.str().c_str();
}