该模块是对一个描述符需要进行的 IO 事件管理的模块,实现对描述符可读,可写,错误..……事件的管理操作,以及 Poller 模块对描述符进行 IO 事件监控就绪后,根据不同的事件,回调不同的处理函数功能。
之所以需要有该模块,是为了更方便的在编码的时候进行一个文件描述符对应事件的维护处理,比如说对一个文件描述符可写事件的设置,这个动作是很频繁的,我们可以将其封装成接口使用等等情况。
简单地说,就是一个连接监听什么事件、触发什么事件都由该模块处理!
1、成员变量的设计
因为我们要对事件进行管理,并且使用的是 epoll 模型,所以就要有一个文件描述符 _fd。
而对这些事件的管理其实就是在操作的时候用按位或操作将要管理的事件添加上即可,而这些事件比如说可读事件 EPOLLIN 等等,其实都是一个 uint32_t 的数据类型,所以我们的成员变量也就需要一个 uint32_t 的数据来存放要管理的事件!
因为触发后我们需要有对应的回调事件,所以可以用包装器创建几个回调函数成员,方便设置和调用,这个包装器中设置的回调函数的类型就涉及到了 Connection 模块的指针,因为 Channel 模块中这些回调函数其实是通过 Connection 模块设置的,如下图所示:

此外,在连接建立完成之后,就需要将 Channel 模块中的监控事件挂到 EventLoop 中,所以势必会和 EventLoop 模块产生联系,因为 EventLoop 模块就是对所有连接的监控事件的管理,所以必须要通知 EventLoop 模块,所以 Channel 模块中还需要一个 EventLoop 模块的指针,方便找到其模块中的添加、移除、修改事件监控的接口,如下图所示:

但是因为 EventLoop 模块还没实现,这里就先不给出该成员变量!
2、成员接口的设计
对于功能设计我们分为两块:对文件描述符监控事件的管理、对文件描述符监控事件触发后的处理。
- 对文件描述符监控事件的管理:
- 判断描述符释放可读
- 判断描述符释放可写
- 设置描述符监控可读
- 设置描述符监控可写
- 解除可读事件的监控
- 解除可写事件的监控
- 解除所有事件的监控
- ……
- 对文件描述符监控事件出发后的处理:
- 需要处理的事件:可读、可写、挂断、错误、任意事件(其实对应的就是各各宏)
- 设置对于不同事件的回调处理函数,明确触发了某个事件之后应该如何处理。
但是因为这里接口还涉及到与 EventLoop 的联系,所以还没办法实现完善,具体的完善接口设计,会在后面 Poller 类以及 EventLoop 类的联调中给出!
所以当前的设计成员如下所示:
using eventcallback_t = std::function<void()>; // 事件触发的函数类型
class Channel
{
private:
int _fd; // 文件描述符
uint32_t _events; // 当前需要监控的事件
uint32_t _revents; // 当前触发或者就绪的事件(由外部设置)
eventcallback_t _read_callback; // 可读事件被触发的回调函数
eventcallback_t _write_callback; // 可写事件被触发的回调函数
eventcallback_t _error_callback; // 错误事件被触发的回调函数
eventcallback_t _close_callback; // 关闭事件被触发的回调函数
eventcallback_t _arbitrary_callback; // 任意事件被触发的回调函数
public:
Channel(int fd)
: _fd(fd), _events(0), _revents(0)
{}
int get_fd(); // 获取文件描述符
uint32_t get_events(); // 获取当前监控的事件
void set_revents(uint32_t revents); // 设置实际就绪的事件(就绪事件其实由EventLoop管理,只是触发后来通知Channel模块调用回调函数而已)
// 设置对应触发事件的回调函数
void set_read_callback(const eventcallback_t& cb);
void set_write_callback(const eventcallback_t& cb);
void set_error_callback(const eventcallback_t& cb);
void set_close_callback(const eventcallback_t& cb);
void set_arbitrary_callback(const eventcallback_t& cb);
bool is_read_able(); // 当前是否监控了可读
bool is_write_able(); // 当前是否监控了可写
void enable_read(); // 启动读事件监控
void enable_write(); // 启动写事件监控
void disable_read(); // 关闭读事件监控
void disable_write(); // 关闭写事件监控
void disable_all(); // 关闭所有事件监控
void handler(); // 事件总处理函数。一旦触发了事件,就调用这个函数,而触发了什么事件如何处理由连接管理者决定
void update(); // 添加或者修改事件监控(需要和EventLoop模块联系起来)
void remove(); // 移除事件监控(需要和EventLoop模块联系起来)
};3、接口的实现
其实实现并不难,就是一些事件的设置等等,最重要的还是 _handler() 函数中对触发事件的管理!
using eventcallback_t = std::function<void()>; // 事件触发的函数类型
class Channel
{
private:
int _fd; // 文件描述符
uint32_t _events; // 当前需要监控的事件
uint32_t _revents; // 当前触发或者就绪的事件(由外部设置)
eventcallback_t _read_callback; // 可读事件被触发的回调函数
eventcallback_t _write_callback; // 可写事件被触发的回调函数
eventcallback_t _error_callback; // 错误事件被触发的回调函数
eventcallback_t _close_callback; // 关闭事件被触发的回调函数
eventcallback_t _arbitrary_callback; // 任意事件被触发的回调函数
public:
Channel(int fd)
: _fd(fd), _events(0), _revents(0)
{}
~Channel()
{
close(_fd); // 记得要释放文件描述符
}
int get_fd() { return _fd; } // 获取文件描述符
uint32_t get_events() { return _events; } // 获取当前监控的事件
void set_revents(uint32_t revents) { _revents = revents; } // 设置实际就绪的事件
// 设置对应触发事件的回调函数
void set_read_callback(const eventcallback_t& cb) { _read_callback = cb; }
void set_write_callback(const eventcallback_t& cb) { _write_callback = cb; }
void set_error_callback(const eventcallback_t& cb) { _error_callback = cb; }
void set_close_callback(const eventcallback_t& cb) { _close_callback = cb; }
void set_arbitrary_callback(const eventcallback_t& cb) { _arbitrary_callback = cb; }
bool _is_read_able() { return (_events & EPOLLIN); } // 当前是否监控了可读
bool _is_write_able() { return (_events & EPOLLOUT); } // 当前是否监控了可写
// 启动读事件监控
void enable_read()
{
_events |= EPOLLIN;
update(); // 实际更新涉及到epoll操作,所以交给Poller来实现
}
// 启动写事件监控
void enable_write()
{
_events |= EPOLLOUT;
update(); // 实际更新涉及到epoll操作,所以交给Poller来实现
}
// 关闭读事件监控
void disable_read()
{
_events &= (~EPOLLIN);
update(); // 实际更新涉及到epoll操作,所以交给Poller来实现
}
// 关闭写事件监控
void disable_write()
{
_events &= (~EPOLLOUT);
update(); // 实际更新涉及到epoll操作,所以交给Poller来实现
}
// 关闭所有事件监控
void disable_all()
{
_events = 0;
update(); // 实际更新涉及到epoll操作,所以交给Poller来实现
}
// 清除所有的回调函数
void clear_callback()
{
_read_callback = _write_callback = _error_callback = _close_callback = _arbitrary_callback = nullptr;
}
// 事件总处理函数。一旦触发了事件,就调用这个函数,而触发了什么事件如何处理由连接管理者决定
void handler()
{
// 下面因为错误和关闭事件触发的时候会释放连接,此时就不能再调用_arbitrary_callback了,所以需要提前先调用
if((_revents & EPOLLIN) || (_revents & EPOLLRDHUP) ||(_revents & EPOLLPRI))
{
// 如果是有数据可读、对端关闭写入、有带外数据的事件触发的话,则都属于是可读事件处理
if(_read_callback)
_read_callback();
if(_arbitrary_callback)
_arbitrary_callback(); // 不管任何事件,都调用的回调函数
}
// 下面的三个事件有可能会释放连接,所以只能处理一个,要用else if连接
if(_revents & EPOLLOUT)
{
if(_write_callback)
_write_callback(); // 可读事件触发的处理
if(_arbitrary_callback)
_arbitrary_callback(); // 不管任何事件,都调用的回调函数
}
else if(_revents & EPOLLERR)
{
if(_arbitrary_callback)
_arbitrary_callback(); // 不管任何事件,都调用的回调函数
if(_error_callback)
_error_callback(); // 错误事件触发的处理
}
else if(_revents & EPOLLHUP)
{
if(_arbitrary_callback)
_arbitrary_callback(); // 不管任何事件,都调用的回调函数
if(_close_callback)
_close_callback(); // 关闭事件触发的处理
}
}
// 添加或者修改事件监控
void update();
// 移除事件监控
void remove();
};