1.Channel
(1)Channel的定位
Channel 是一个“事件监听器”和“回调调度员”。
它内部持有:
a.一个文件描述符(fd_)——比如listenfd或connfd。
b.它关心的事件(events_)——想监听读?写?
c.实际发生的事件(revents_)——epoll返回的实际事件。
d.事件发生后的处理函数(readCallback_/writeCallback_)。
在整个架构中,evenloop负责驱动循环,epollPoller负责调用epoll_wait监控事件,Channel是具体的操作者,把fd、事件、回调打包在一起,当这个fd出现时,由Channel调用回调函数。
Channel.h
#ifndef MUDUO_CHANNEL_H #define MUDUO_CHANNEL_H #include <functional> //前向声明,作用,告诉编译器“有EventLoop这个类,它定义在别处”。 //这里只需要EventLoop*指针,不需要完整定义,避免头文件循环包含。 class EventLoop; //Channel封装了一个fd及其关心的事件和回调 class Channel { public: //回调函数类型:无参数无返回值的函数,用来装readCallback_和writeCallback_。 //为什么无参?因为回调执行时,Channel已经把需要的信息都存到了自己的成员变量里,回调 //可以直接访问,不需要参数 using EventCallback = std::function<void()>; //构造时传入Channel所属的EventLoop的指针和fd,初始化成员变量。 Channel(EventLoop* loop, int fd); ~Channel(); //设置回调函数(外部传入) //由TcpConnection在构造时调用,把handleRead和handleWrite绑定进来。 //关键解耦点:Channel本身不知道业务逻辑是什么,它只负责在事件发生时调用这些回调。 void setReadCallback(const EventCallback& cb) { readCallback_ = cb; } void setWriteCallback(const EventCallback& cb) { writeCallback_ = cb; } //获取基本信息 int fd() const { return fd_; } //返回管理的文件描述符 int events() const { return events_; } //返回当前关心的事件掩码(比如kReadEvent) int revents() const { return revents_; } //返回实际发生的事件掩码() //Poller 用这个设置"实际发生了什么事件" //由 EpollPoller::poll() 在 epoll_wait 返回后调用,把内核返回的事件写入 revents_。 //之后 EventLoop 会调用 channel->handleEvent(),handleEvent 通过 revents_ 判断发生了什么 //事件。 void set_revents(int revt) { revents_ = revt; } //|=(按位或):开启某个事件。events_ |= 1 表示把读事件位设为 1。 //&= + ~(按位与 + 取反):关闭某个事件。events_ &= ~1 表示把读事件位清 0。 //调用 update():每次修改 events_ 后,都要通知 EventLoop 去更新 epoll 的注册。 //启用/禁用读监听 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(); } //判断当前状态 //用来判断当前是否在监听读/写事件,在 handleWrite 里经常用到(比如:数据发完后,如果还在监听 //写事件,就取消它)。 bool isWriting() const { return events_ & kWriteEvent; } bool isReading() const { return events_ & kReadEvent; } //更新到 epoll (通过 EventLoop 转发给 Poller) void update(); //处理事件(epoll_wait 返回后调用) void handleEvent(); private: //事件类型常量(对应epoll的EPOLLIN/EPOLLOUT) //值的选择刻意与epoll的宏对齐。 //为什么要自定义而不是直接用 EPOLLIN? //为了屏蔽底层细节。Channel 层只认 kReadEvent,不管底层是 epoll、poll 还是 select。 //将来如果要换 I/O 多路复用机制,只需要在 Poller 层做翻译,Channel 的代码完全不用改。这就是 //muduo 的“可扩展性”设计。 static const int kNoneEvent = 0; static const int kReadEvent = 1; // EPOLLIN static const int kWriteEvent = 4; // EPOLLOUT static const int kErrorEvent = 8; // EPOLLERR | EPOLLHUP EventLoop* loop_;//所属的EventLoop(所有 I/O 操作都在这个线程里)。 int fd_; // 这个Channel管理的文件描述符 int events_; // 关心什么事件(读?写?) int revents_; //实际发生了什么事件(epoll_wait返回的) EventCallback readCallback_; EventCallback writeCallback_; }; #endifChannel.cpp
#include "Channel.h" #include "EventLoop.h" Channel::Channel(EventLoop* loop, int fd) : loop_(loop) , fd_(fd) , events_(0) , revents_(0) { } Channel::~Channel() {} //修改了关心的事件之后,更新到epoll void Channel::update() { //EventLoop会转发给Poller(下一步实现) loop_->updateChannel(this); } //epoll_wait返回后,根据发生的事件调用对应回调 void Channel::handleEvent() { // 检查错误事件(由 Poller 层转换后的 kErrorEvent) // 错误事件优先级最高,先处理 if (revents_ & kErrorEvent) { if (errorCallback_) { errorCallback_(); } return; // 出错后不再处理其他事件 } //如果发生的是读事件,执行读回调 if (revents_ & kReadEvent) { if (readCallback_) { readCallback_(); } } //如果发生的是写事件,执行写回调 if (revents_ & kWriteEvent) { if (writeCallback_) { writeCallback_(); } } }//修改了关心的事件之后,更新到epoll void Channel::update() { //EventLoop会转发给Poller(下一步实现) loop_->updateChannel(this); }作用 :通知`EventLoop` 更新 epoll 注册
- - 调用`loop_->updateChannel(this)`
- - `EventLoop` 会转发给`Poller`
- - `Poller` 调用`epoll_ctl` 更新注册
调用链 :
Channel::update()
→ EventLoop::updateChannel(channel)
→ Poller::updateChannel(channel)
→ epoll_ctl(epollfd_, operation, fd, &ev)
//epoll_wait返回后,根据发生的事件调用对应回调 void Channel::handleEvent() { // 检查错误事件(由 Poller 层转换后的 kErrorEvent) // 错误事件优先级最高,先处理 if (revents_ & kErrorEvent) { if (errorCallback_) { errorCallback_(); } return; // 出错后不再处理其他事件 }作用 :处理错误事件
- - `revents_ & kErrorEvent` :检查是否有错误事件
- - `errorCallback_()` :调用错误回调
- - `return` :出错后不再处理其他事件
为什么错误事件优先级最高?
- - 错误发生时,连接可能已经不可用
- - 继续处理读/写事件可能导致崩溃
- - 应该立即关闭连接
//如果发生的是读事件,执行读回调 if (revents_ & kReadEvent) { if (readCallback_) { readCallback_(); } }作用 :处理读事件
- - `revents_ & kReadEvent` :检查是否有读事件
- - `readCallback_()` :调用读回调
使用场景 :
- - `TcpConnection` :读取数据
- - `Acceptor` :接受新连接
- - `EventLoop` :读取 eventfd(跨线程唤醒)
- - `TimerQueue` :读取 timerfd(定时器超时)
//如果发生的是写事件,执行写回调 if (revents_ & kWriteEvent) { if (writeCallback_) { writeCallback_(); } } }作用 :处理写事件
- - `revents_ & kWriteEvent` :检查是否有写事件
- - `writeCallback_()` :调用写回调
使用场景 :
- - `TcpConnection` :继续发送缓冲的数据