目录
0.简介
最近才拜读了《Unix网络编程卷1》的前15章,书很有用,但也很枯燥。Talk is cheap, show me your code. 于是找了muduo这个网络库来研究研究。
muduo基于reactor模式, 是一个多线程的c++网络库,只支持linux上的tcp协议. 实现上,采用 "one thread, one loop" , 到处充斥着回调函数, 看习惯了就好。服务端大致类图如下:
1. 各个类的作用:
TcpServer
一般运行在主线程,由用户创建和销毁。功能包括监听套接口,创建事件循环线程池,创建并保存新连接TcpConnection, 以及和用户代码交互(通过xxxCallback 函数)。
Acceptor
主要负责监听连接请求,调用listen() 接口时,通过Channel::enableReading() 把socket的描述符加到poller(I/O复用器)中。当有新连接到达时,先调用系统函数accept,再回调函数 newConnectionCallback_ 让TcpServer去创建连接。
TcpConnection
这个类同时被服务端和客户端使用。建立连接后,通过Channel::enableReading() 把socket的描述符加到poller(I/O复用器)中,然后进行正常的TCP 数据收发。基本上一个TcpConnection对应一个Channel.
EventLoopThreadPool
管理所有客户端连接的线程池,每个线程都有唯一一个事件循环。可以调用setThreadNum设置线程的数目。
EventLoopThread
事件循环线程, 包含一个Thread对象,一个EventLoop对象。在构造函数中,把EventLoopThread::threadFunc 注册到Thread对象中(线程启动时会调用EventLoopThread::threadFunc),线程启动如下:
EventLoop* EventLoopThread::startLoop()
{
assert(!thread_.started());
thread_.start(); // 这个时候loop_为NULL, 然后启动线程
{
MutexLockGuard lock(mutex_);
while (loop_ == NULL)
{
cond_.wait(); // 如果loop_为NULL,就等待, 直到线程完全启动,调用下面的threadFunc(), loop_ 被创建
}
}
return loop_;
}
void EventLoopThread::threadFunc()
{
EventLoop loop;
if (callback_)
{
callback_(&loop);
}
{
MutexLockGuard lock(mutex_);
loop_ = &loop;
cond_.notify(); // loop_创建了,通知startLoop()函数
}
loop.loop();
//assert(exiting_);
loop_ = NULL;
}
以下三个是reactor模式的核心类:
EventLoop
线程启动后,事件循环就开始跑起来。首先调用Poller:poll(阻塞调用) 接口查看I/O 可读/可写情况,获得所有活跃的channel, 执行每个channel的handleEvent函数,这个handleEvent最终会调用到Accepter或TcpConnection的handleRead / handleWrite, 完成真正的读或写。然后调用doPendingFunctors 按顺序执行 队列中的函数。
void EventLoop::loop()
{
assert(!looping_);
assertInLoopThread(); // loop()函数的调用者必须是EventLoop对象的创建者
looping_ = true;
quit_ = false; // FIXME: what if someone calls quit() before loop() ?
LOG_TRACE << "EventLoop " << this << " start looping";
while (!quit_) // 如果不退出,就一直循环
{
activeChannels_.clear();
pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_); // 获得所有可用的channels
++iteration_;
if (Logger::logLevel() <= Logger::TRACE)
{
printActiveChannels();
}
// TODO sort channel by priority
eventHandling_ = true;
for (ChannelList::iterator it = activeChannels_.begin();
it != activeChannels_.end(); ++it)
{
currentActiveChannel_ = *it;
currentActiveChannel_->handleEvent(pollReturnTime_); // 执行handleEvent
}
currentActiveChannel_ = NULL;
eventHandling_ = false;
doPendingFunctors(); // 执行队列中的函数
}
LOG_TRACE << "EventLoop " << this << " stop looping";
looping_ = false;
}
Poller
这是muduo中唯一的抽象类,被PollPoller和EpollPoller实现,内部分别使用Linux的poll和epoll系统函数来做I/O轮询