文章目录
- 一、Channel类
- 二、Poller/EpollPoller类
- 三、EventLoop类
- 四、Thread、EventLoopThread、EventLoopThreadPool类
- 五、Acceptor类
- 六、Buffer类
- 七、TcpConnection类
- 八、TcpServer类
mainReactor就是我们代码中的mainloop(baseloop),subReactor是我们代码中的工作线程进行读写事件的处理,read是底层做的,decode、compute、encode是用户做的。从网络上read数据,然后从网络上send数据,这些是由muduo库的。decode、compute、encode是用户在OnMessage处理的业务逻辑,每一个线程执行一个独立的Reactor
muduo库的核心模块包括:Channel、Poller/EpollPoller、EventLoop、Thread、EventLoopThread、EventLoopThreadPool、Socket、Acceptor、Buffer、TcpConnection、TcpServer
一、Channel类
Channel主要是封装了以下成员:
- fd_:需要注册在Poller(poll/epoll)的用于和用户通信的文件描述符
- events_:fd感兴趣的事件
- revents_:fd发生的事件
- loop_:Channel归属的事件循环EventLoop
- callback_:发生事件时,根据Poller通知的revents执行的回调函数
- index_:用于标识Chnnel的状态
我们总共只有两种Channel(acceptorChannel和connChannel),因为总共只有两种fd,分别是用于监听新用户连接的listenfd和用于监听已连接用户读写事件的connfd
二、Poller/EpollPoller类
相当于是事件分发器Demultiplex
Poller封装了以下成员:
- channels_:一个事件循环EventLoop中包含一个Poller,这个Poller负责管理channels_,负责监听这些channels_上的事件,有事件发生时,会把有事件发生的Channel放入EventLoop::activateChannels_,然后ownerLoop_通过activateChannels_调用处理事件的回调。Channel和Poller不能直接通信,通过EventLoop通信
- ownerLoop_:Poller所属的EventLoop,Poller会填充有事件发生的Channel到ownerLoop_的成员到activateChannels_
void EventLoop::loop(){
looping_ = true;
quit_ = false;
LOG_INFO("EventLoop %p start looping \n", this);
while(!quit_){
activateChannels_.clear();
pollReturnTime_ = poller_->poll(kPollTimeMs, &activateChannels_);
// for循环处理所有发生事件的channel
for(Channel* channel : activateChannels_){
// poller监听哪些channel发生事件,然后上报给EventLoop,EventLoop通知channel调用相应的回调函数处理相应的事件(Channel执行回调)
channel->handleEvent(pollReturnTime_);
}
// subReactor给唤醒后,执行需要处理的回调操作(mainReactor事先注册的),接收新用户的Channel
doPendingFunctors();
}
LOG_INFO("EventLoop %p stop loop \n", this);
looping_ = false;
}
三、EventLoop类
- activateChannels_:包含了所有发生事件的Channel,Poller会把有事件发生的Channel放入EventLoop::activateChannels
- wakeupFd_和wakeupChannel_:一个wakeupFd_隶属于一个EventLoop,每一个wakeupFd_都是注册在了EventLoop管理的Poller上。对于subloop来说,wakeupChannel_用于和mainloop通信,activateChannels用于已连接的客户端通信,如果activateChannels没有事件发生,subloop就会一直阻塞监听,而此时有新的客户连接,mainloop需要找一个subloop处理这个已连接用户的读写事件,就需要往这个subloop的wakeupFd_上写8字节数据唤醒
- pendingFunctors_:每一个回调函数都需要在发生事件的EventLoop所属的线程执行,如果当前loop是属于当前执行的线程,那直接执行回调函数,否则就把回调函数存到pendingFunctors这个vector里面,然后唤醒相应loop所在线程。然后被唤醒的线程会执行在pendingFunctor里所有的函数
四、Thread、EventLoopThread、EventLoopThreadPool类
EventLoopThreadPool类的成员如下:
getNextLoop方法:通过轮询算法获取下一个subloop,如果用户没有使用setThreadNum设置subloop线程的数量,那就没有创建subloop,getNextLoop返回的一直是baseloopEventLoopThread类的成员如下:
EventLoopThread封装了线程Thread以及对应的事件循环EventLoop,Thread封装了std::threadThread类的成员如下:
TcpServer启动的时候,会让自己的成员threadPool_也启动
线程池启动时,会创建事件循环线程EventLoopThread,让该EventLoopThread调用startLoop方法
接着会让事件循环对应的Thread类线程启动
这里的thread_.start方法就会调用thread_生成时传入的回调函数,即EventLoopThread::threadFunc
这样就启动了事件循环,即epoll_wait开始监听事件
五、Acceptor类
- loop_:Acceptor所属的事件循环,即mainLoop
- acceptSocket_、acceptChannel_:封装了listenfd,用于监听新用户连接
Acceptor工作在mainLoop,主要封装了listenfd相关操作,包括创建socket、bind、listen
六、Buffer类
Buffer类的成员如下:
缓冲区里有kCheapPrepend、readeridx、writeridx,这3个成员,[readeridx, writeridx]表示可读数据区间
prependable有8个字节,是头部信息,用于记录数据包的长度,刚开始readeridx和writeridx都在这8个字节处的位置,然后我们写数据进去,写好的东西就可以是待发送的东西,由Buffer专门进行在缓冲区读取数据进行发送,我们从缓冲区可以进行send发送,用户也可以不断从缓冲区读取数据,通过readeridx和writeridx处理
Buffer是缓冲区,对于nonblocking(非阻塞)的I/O,我们都需要设置缓冲区,涉及到应用写数据写到缓冲区,write函数将应用程序缓冲区的数据再写到TCP的发送缓冲区,最后发送数据
如果应用程序调用write直接写入TCP的发送缓冲区,写满了就要发送,应用程序就要暂停写入等待发送完成,这是同步的过程,效率比较慢。 如果有缓冲区的话,应用程序可以随意的向应用程序缓冲区写数据,等TCP缓冲区空了Buffer再将应用程序缓冲区中的数据写入TCP缓冲区
应用产生数据快,tcp发送的慢,难道每一次发送数据都要等tcp发送完上一次的数据再发送当前数据?
写数据到Buffer,然后让系统去调用write从Buffer写入TCP发送缓冲区,或者系统调用read从TCP接收缓冲区写入Buffer。相当于应用发送或者接收数据和tcp真实发送或者接收数据就成异步的了,这样非常高效
七、TcpConnection类
封装了socket,Channel,和一系列回调操作,高水位线的控制(不要发送过快),发送和接收缓冲区
对于Channel执行的回调,都是由TcpConnection来设置的
八、TcpServer类