这一章从0开始实现一个类似muduo的基于Reactor模式的C++网络库
传统的Reactor通过控制select(2)和poll(2)的等待时间来实现定时,而现在在Linux中有了timerfd,我们可以处理IO事件相同的方式来处理定时,代码的一直性要求的更好
muduo的定时器功能由三个class实现,TimeId、Timer、TimeQueue。用户只能看到第一个class,另外两个都是内部实现的细节使用的数据结构是std::setTimerQueue使用了一个Channel来观察timerfd_上的readable事件EvenLoop在它的IO线程内执行某个用户任务回调即runInLoop(),如果用户在当前IO线程调用这个函数,回调会同步进行;如果用户在其他线程调用该函数,回调函数cb会加入到队列,IO线程会被唤醒来调用这个Functor
void EventLoop::runInLoop(const Functor& cb) { if (isInLoopThread()) { cb(); } else { queueInLoop(cb); } }有了这个功能,我们就能够轻易地在线程间调配任务,比方说吧TimerQueue的成员函数调用移动到IO线程,这样可以在不用锁的情况下保证线程安全性
IO线程平时阻塞到事件循环EvenLoop::loop()的poll(2)调用中,为了让IO线程立刻唤醒它,传统的方法是使用pipe(2),IO线程始终监视此管道的可读事件,需要唤醒的时候,其他线程网管道里写一个字节。** 现在的Linux有了eventfd(2),可以更加高效的唤醒,因为不必管理缓冲区
EventLoopThread class
一个线程可以有不止一个IO线程,可以按照优先级将不同的Socket分给不同的IO线程,避免优先级反转,为了方便将来使用故定义了此类
EventLoopThread 会启动自己的线程,并在其中运行EventLoop::loop();
ventLoop* EventLoopThread::startLoop() { assert(!thread_.started()); thread_.start(); { MutexLockGuard lock(mutex_); while (loop_ == NULL) { cond_.wait(); } } return loop_; } void EventLoopThread::threadFunc() { EventLoop loop; { MutexLockGuard lock(mutex_); loop_ = &loop; cond_.notify(); } loop.loop(); //assert(exiting_); }本节开始,逐步实现一个非阻塞TCP网络库
Acceptor class
用于accept(2)新TCP连接,并通过回调通知使用者,是内部类,供TcpServer使用,声明周期由后者控制。
TcpServer class
它的功能是管理accept(2)获得的TcpConnection。Tcpserver是供用户直接使用的,生命周期由用户控制用户只需要设置好callback,调用start()即可TcpServer内部使用Acceptor来获得新连接的fd。它保存用户提供的Connection callback和MessageCallBack。每个TcpConnection对象都有一个名字,这个名字是由其所属的TcpServer在创建TcpConnection对象时生成的,名字是ConnectionMap的keyTcpConnecttion class
该类时muduo里面最复杂,最核心的class
是muduo里面唯一默认使用shared_ptr来管理的class,也是唯一继承enable_shared_from_this的classmuduo里面只有一种关闭连接的方式,被动关闭,即对方先关闭连接,本地read(2)返回0,触发关闭逻辑