Muduo类详解之Channel

简介: Muduo类详解之Channel

       在 TCP ⽹络编程中,想要通过 IO 多路复⽤( epoll / poll )监听某个⽂件描述符,就需要把这个 fd 和该 fd 感兴趣 的事件通过 epoll_ctl 注册 到 IO 多路复⽤模块上。当 IO 多路复⽤模块监听到该 fd 发⽣了某个事件。事件监听 器返回发⽣事件的 fd 集合(有哪些 fd 发⽣了事件)以及每个 fd 的事件集合(每个 fd 具体发⽣了什么事件)。

       Channel 类则封装了⼀个 fd 和这个 fd 感兴趣事件以及 IO 多路复⽤模块监听到的每个 fd 的事件集合。同时 Channel类还提供了 设置 该 fd 的感兴趣事件,以及将该 fd 及其感兴趣事件 注册 到事件监听器或从事件监听器上移 除,以及保存了该 fd 的每种事件对应的处理函数。

       每个 Channel 对象只属于⼀个 EventLoop ,即只属于⼀个 IO 线程。只负责⼀个⽂件描述符( fd )的 IO 时间分 发,但不拥有这个 fd 。 Channel 把不同的 IO 事件分发为不同的回调,回调⽤ C++11 的特性 function 表示。声明周期由拥有它的类负责。

Channel类其实相当于一个文件描述符的保姆!

 在TCP网络编程中,想要IO多路复用监听某个文件描述符,就要把这个fd和该fd感兴趣的事件通过epoll_ctl注册到IO多路复用模块(我管它叫事件监听器)上。当事件监听器监听到该fd发生了某个事件。事件监听器返回发生事件的fd集合]以及[每个fd都发生了什么事件。

 Channel类则封装了一个 [fd] 和这个 [fd感兴趣事件] 以及事件监听器监听到 [该fd实际发生的事件]。同时Channel类还提供了设置该fd的感兴趣事件,以及将该fd及其感兴趣事件注册到事件监听器或从事件监听器上移除,以及保存了该fd的每种事件对应的处理函数。


Channel类重要的成员变量:


  • fd_这个Channel对象照看的文件描述符 int events_代表fd感兴趣的事件类型集合 int
  • revents_代表事件监听器实际监听到该fd发生的事件类型集合,当事件监听器监听到一个fd发生了什么事件,通过Channel::set_revents()函数来设置revents值。
  • EventLoop* loop:这个fd属于哪个EventLoop对象,这个暂时不解释。
  • read_callback_、write_callback_、close_callback_、error_callback_:这些是std::function类型,代表着这个Channel为这个文件描述符保存的各事件类型发生时的处理函数。比如这个fd发生了可读事件,需要执行可读事件处理函数,这时候Channel类都替你保管好了这些可调用函数,真是贴心啊,要用执行的时候直接管保姆要就可以了
class Channel {
public:
 typedef std::function<void()> EventCallBack;
 Channel();
 explicit Channel(int fd);
 ~Channel();
 // IO事件回调函数的调⽤接⼝
代码随想录知识星球
从 Channel 的类定义中可以看出,每个 Channel 持有⼀个⽂件描述符,正在监听的事件,已经发⽣的事件(由
Poller 返回),以及各个事件(读、写、更新、错误)回调函数的 Function 对象。
 // EventLoop中调⽤Loop开始事件循环 会调⽤Poll得到就绪事件
 // 然后依次调⽤此函数处理就绪事件
 void HandleEvents(); 
 void HandleRead(); // 处理读事件的回调
 void HandleWrite(); // 处理写事件的回调
 void HandleUpdate(); // 处理更新事件的回调
 void HandleError(); // 处理错误事件的回调
 int get_fd();
 void set_fd(int fd);
 // 返回weak_ptr所指向的shared_ptr对象
 std::shared_ptr<http::HttpConnection> holder();
 void set_holder(std::shared_ptr<http::HttpConnection> holder);
 
 // 设置回调函数
 void set_read_handler(EventCallBack&& read_handler);
 void set_write_handler(EventCallBack&& write_handler);
 void set_update_handler(EventCallBack&& update_handler);
 void set_error_handler(EventCallBack&& error_handler);
 
 void set_revents(int revents);
 int& events();
 void set_events(int events);
 int last_events();
 bool update_last_events();
private:
 int fd_; // Channel的fd
 int events_; // Channel正在监听的事件(或者说感兴趣的时间)
 int revents_; // 返回的就绪事件
 int last_events_; // 上⼀此事件(主要⽤于记录如果本次事件和上次事件⼀样 就没必要调⽤
epoll_mod)
 // weak_ptr是⼀个观测者(不会增加或减少引⽤计数),同时也没有重载->,和*等运算符 所以不能直接使⽤
 // 可以通过lock函数得到它的shared_ptr(对象没销毁就返回,销毁了就返回空shared_ptr)
 // expired函数判断当前对象是否销毁了
 std::weak_ptr<http::HttpConnection> holder_; 
 EventCallBack read_handler_; 
 EventCallBack write_handler_;
 EventCallBack update_handler_;
 EventCallBack error_handler_;
};

       从 Channel 的类定义中可以看出,每个 Channel 持有⼀个⽂件描述符,正在监听的事件,已经发⽣的事件(由Poller 返回),以及各个事件(读、写、更新、错误)回调函数的 Function 对象。

     

总的来说, Channel 就是对 fd 事件的封装,包括注册它的事件以及回调。 EventLoop 通过调⽤

Channel::handleEvent() 来执⾏ Channel 的读写事件。 Channel::handleEvent() 的实现也⾮常简单,就 是⽐较已经发⽣的事件(由 Poller 返回),来调⽤对应的回调函数(读、写、错误)。  

// IO事件的回调函数 EventLoop中调⽤Loop开始事件循环 会调⽤Poll得到就绪事件
// 然后依次调⽤此函数处理就绪事件
void Channel::HandleEvents() {
 events_ = 0;
 // 触发挂起事件 并且没触发可读事件
 if ((revents_ & EPOLLHUP) && !(revents_ & EPOLLIN)) {
 events_ = 0;
 return;
 }
 // 触发错误事件
 if (revents_ & EPOLLERR) {
 HandleError();
 events_ = 0;
 return;
 }
 // 触发可读事件 | ⾼优先级可读 | 对端(客户端)关闭连接
 if (revents_ & (EPOLLIN | EPOLLPRI | EPOLLRDHUP)) {
 HandleRead();
 }
 // 触发可写事件
 if (revents_ & EPOLLOUT) {
 HandleWrite();
 }
 //处理更新监听事件(EpollMod)
 HandleUpdate();
}
相关文章
|
6月前
muduo源码剖析之channel通道类
channel是muduo中的事件分发器,它只属于一个EventLoop,Channel类中保存着IO事件的类型以及对应的回调函数,每个channel只负责一个文件描述符,但它并不拥有这个文件描述符。channel是在epoll和TcpConnection之间起沟通作用,故也叫做通道,其它类通过调用channel的setCallbcak来和建立channel沟通关系。
103 0
|
6月前
muduo源码剖析之Buffer缓冲区类
Buffer封装了一个可变长的buffer,支持廉价的前插操作,以及内部挪腾操作避免额外申请空间使用vector作为缓冲区(可自动调整扩容)
70 0
muduo源码剖析之Buffer缓冲区类
|
6月前
muduo源码剖析之Socket类
封装了一个sockfd相关的设置。比较简单,已经编写注释。
54 0
|
6月前
muduo源码剖析之Acceptor监听类
Acceptor类用于创建套接字,设置套接字选项,调用socket()->bind()->listen()->accept()函数,接受连接,然后调用TcpServer设置的connect事件的回调。listen()//在TcpServer::start中调用封装了一个listen fd相关的操作,用于mainLoop接受器封装,实质上就是对Channel的多一层封装监听连接 当新连接进入时,调用Socket::accept创建套接字,触发TcpServer的回调TcpServer通过该接口设置回调,
44 0
|
5月前
|
调度 PHP
Swoole 源码分析之 Channel 通道模块
通道,用于协程间通讯,支持多生产者协程和多消费者协程。底层自动实现了协程的切换和调度。通道与 PHP 的 Array 类似,仅占用内存,没有其他额外的资源申请,所有操作均为内存操作,无 IO 消耗。
110 5
Swoole 源码分析之 Channel 通道模块
|
6月前
Muduo类详解之Poller
Muduo类详解之Poller
|
6月前
Muduo类详解之EventLoop
Muduo类详解之EventLoop
|
6月前
|
网络协议
muduo源码剖析之TcpClient客户端类
muduo用TcpClient发起连接,TcpClient有一个Connector连接器,TCPClient使用Conneccor发起连接, 连接建立成功后, 用socket创建TcpConnection来管理连接, 每个TcpClient class只管理一个TcpConnecction,连接建立成功后设置相应的回调函数。很显然,TcpClient用来管理客户端连接,真正连接交给Connector。
84 0
muduo源码剖析之TcpClient客户端类
|
6月前
|
Java 容器
muduo源码剖析之TcpServer服务端
TcpServer拥有Acceptor类,新连接到达时new TcpConnection后续客户端和TcpConnection类交互。TcpServer管理连接和启动线程池,用Acceptor接受连接。服务端封装 - muduo的server端维护了多个tcpconnection注意TcpServer本身不带Channel,而是使用Acceptor的Channel。
65 0
muduo源码剖析之TcpServer服务端
|
6月前
|
API
muduo源码剖析之poller/EpollPoller多路复用类
poller是I/O多路复用接口抽象虚基类,对I/O多路复用API的封装,muduo提供了EPollPoller和PollPoller派生类(epoll和poll),所以不支持select.newDefaultPoller()默认选择epoll。
76 0