主要参考资料:
我在地铁吃闸机
基础处理框架:Multi-reactor
muduo库有三个核心组件实现持续监听reactor的fd:channel;epoll/poller/eventloop类
channel
事件监听器epoll_ctl监听到了fd发生了什么事件,channel类会封装每个fd和fd感兴趣的事件以及监听器返回的fd实际发生的事件。同时channel类提供了fd感兴趣事件,并将其注册到事件监听器(移除),保存了fd每种事件的处理函数
channel类重要成员变量
fd_该channel照看的文件描述符
events_该fd感兴趣事件类型集合
revents_该fd实际发生的事件类型集合,通过set_revents()来设置,并封装进channel里
event_loop该fd属于哪个loop
_callback:fd发生事件后的处理函数
void HandlerEvent(Timestamp receivetime);当调用epoll_wait()之后,该函数让每个channel根据自己照顾的fd实际发生的事件和感兴趣的事件调用处理函数
poller/EpollPoller
在本框架下,一个poller对应一个reactor。负责监听fd是否触发和fd实际发生了什么。他们两个分别都是继承实现。本项目采取epoll。
TimeStamp poll(int timeoutMs, channelList activeChannels)
当外部调用poll方法时,该方法底层是通过epoll_wait获取事件监听器上发生的fd。通过哈希表,channels_可以根据fd找到封装该fd的channel(每个fd都是由一个channel封装的)
,事件发生后,写入channel类中的revents,然后把channel封装进activeChannels(监听结果vector<channel>)
EventLoop
上文提到调用完poller会得到监听结果,而网络服务器需要持续监听持续获取持续处理的能力。所以eventloop负责实现循环。eventloop会整合上面两类组件。
OneloopPerThread
每个eventloop都绑定一个线程(Muduo的特点)利用多核CPU每个核的线程负责循环监听一组文件描述符fd的集合。
调用poller获得监听结果,调用每一个channel为其分配好的处理函数。
acceptor
接受新用户连接并且分发给subreactor
重要成员变量
acceptSocket_是服务器监听套接字的文件描述符
acceptChannel_是channel类,把上边的变量核感兴趣事件核事件对应的处理函数都封装进去。
EventLoop *loop监听套接字的fd由哪个subloop负责循环
newConnectionCallback_公平地选择一个subloop
重要成员方法
listen()开启对acceptSocket_的监听同时将acceptChannel_以及感兴趣的事件注册到mainloop的事件监听器上。
hanleRead()当mainloop监听到acceptChannel上发生了可读事件(新用户连接),就调用该函数。里面还调用了newConnectionCallback_保存的函数。
buffer
封装了用户缓存区,像这个缓冲区读写数据。
vector方便动态扩容。
可支持读一点,写一点。
重要成员函数
append()将数据添加到缓冲区
retrieveAsString(len)获取缓冲区中长度为len的数据
retrieveAllString获取所有
ensureWriteableBytes(len)在写入len长度的数据之前先确定能不能装下
ssize_t Buffer::readFd客户端发来数据,readFd从该TCP接收缓冲区将数据读出来并放入buffer
ssize_t Buffer::writeFd
TcpConnection类
acceptor是在mainloop中对服务器监听套接字fd及其相关方法进行封装(监听,接受链接,分发)
TcpConnection是在subloop中对连接套接字fd及其相关方法进行封装(读消息事件,发送消息事件,连接关闭事件,错误事件)