Channel类和文件描述符一一对应,它保存了所关心的文件描述符(fd_)、关注的事件(从TcpConnection那边注册的readCallback_、writeCallback_等)、poller返回的事件。Channel类对象被传到Poller类中进行poll(), 返回时,成员变量revents_会被改写,handleEvent正是根据这个revents_来执行读/写/出错操作(回调TcpConnection之前注册的函数)。
#ifndef MUDUO_NET_CHANNEL_H
#define MUDUO_NET_CHANNEL_H
#include <muduo/base/noncopyable.h>
#include <muduo/base/Timestamp.h>
#include <functional>
#include <memory>
namespace muduo
{
namespace net
{
class EventLoop;
/********************************************************************
Modify : Eric Lee
Date : 2018-01-15
Description : channel类(事件处理器)负责注册与响应I/O事件,但是它不
拥有文件描述符。每一个channel对象自始至终都只属于一个EventLoop。
*********************************************************************/
class Channel : noncopyable
{
public:
// 事件回调函数
typedef std::function<void()> EventCallback;
// 读事件回调函数
typedef std::function<void(Timestamp)> ReadEventCallback;
Channel(EventLoop* loop, int fd);
~Channel();
// 处理事件
void handleEvent(Timestamp receiveTime);
// 设置读回调函数(参数是TcpConnection注册的)
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); }
// 把当前事件处理器绑定到某一个对象上
void tie(const std::shared_ptr<void>&);
// 返回文件描述符
int fd() const { return fd_; }
// 返回该事件处理所需要处理的事件
int events() const { return events_; }
// 设置实际活动的事件
void set_revents(int revt) { revents_ = revt; }
// 判断是否有事件
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; }
// 返回索引
int index() { return index_; }
// 设置索引
void set_index(int idx) { index_ = idx; }
// 用于调试,把事件转换为字符串
string reventsToString() const;
string eventsToString() const;
// 不记录hup事件
void doNotLogHup() { logHup_ = false; }
// 所属的事件循环(一个EventLoop可以有多个channel,但是一个channel只属于一个EventLoop)
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_; // 实际活动的事件
int index_; // 表示在poll事件数组中的序号
bool logHup_;
std::weak_ptr<void> tie_;
bool tied_;
bool eventHandling_; // 是否正在处理事件
bool addedToLoop_; // 是否已经被添加到事件循环中
// 事件回调
ReadEventCallback readCallback_;
EventCallback writeCallback_;
EventCallback closeCallback_;
EventCallback errorCallback_;
};
} // namespace net
} // namespace muduo
#endif // MUDUO_NET_CHANNEL_H
#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;
// POLLPRI:表示对应的文件描述符有紧急的数据可读
// POLLIN:表示对应的文件描述符可以读
// POLLOUT:表示对应的文件描述符可以写
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),
addedToLoop_(false)
{
}
// 销毁事件处理器
Channel::~Channel()
{
assert(!eventHandling_);
assert(!addedToLoop_);
if (loop_->isInLoopThread())
{
assert(!loop_->hasChannel(this));
}
}
void Channel::tie(const std::shared_ptr<void>& obj)
{
tie_ = obj;
tied_ = true;
}
// 更新通道,调用EventLoop::updateChannel(),接着又会调用poller::updatechannel()
void Channel::update()
{
addedToLoop_ = true;
loop_->updateChannel(this);
}
// 移除通道
void Channel::remove()
{
assert(isNoneEvent());
addedToLoop_ = false;
loop_->removeChannel(this);
}
void Channel::handleEvent(Timestamp receiveTime)
{
std::shared_ptr<void> guard;
if (tied_)
{
guard = tie_.lock();
if (guard) // 确保对象还活着
{
handleEventWithGuard(receiveTime);
}
}
else
{
handleEventWithGuard(receiveTime);
}
}
/********************************************************************
Modify : Eric Lee
Date : 2018-01-15
Description : 根据revents_的值,分别调用不同的回调函数。
*********************************************************************/
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;
}
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();
}