原代码解析
源代码地址:https://gitee.com/lpzdinghai/lpzmuduo
一、整体架构:主从 Reactor 架构
整个库的核心是主 Reactor(mainLoop)+ 从 Reactor(subLoop) 的多线程架构,目的是将连接监听和数据读写解耦,充分利用多核 CPU,提升并发处理能力。
架构核心分工
| 角色 | 所属线程 | 核心作用 | 核心组件 |
|---|---|---|---|
| 主 Reactor | 主线程(main) | 监听客户端新连接、分配连接 | Acceptor + mainLoop |
| 从 Reactor | 工作线程(sub) | 处理已建立连接的所有 IO 事件 | EventLoopThreadPool + subLoop |
| 连接管理 | 跨线程 | 维护 TCP 连接的生命周期 | TcpConnection + Channel |
| IO 复用 | 所有 EventLoop | 监听 fd 事件并上报 | Poller(EPollPoller) |
| 线程管理 | 主线程 | 管理工作线程,创建 subLoop | EventLoopThreadPool + Thread |
架构核心思想
- 主线程只做最轻量化的连接监听,不处理任何业务和数据读写,避免被阻塞;
- 新连接建立后,通过轮询算法分配给某个工作线程的 subLoop,由 subLoop 全程负责该连接的读、写、关闭等所有 IO 事件;
- 每个线程绑定一个独立的 EventLoop(事件循环),线程内所有操作都是单线程无锁的,避免多线程竞争,提升效率;
- 通过wakeupfd实现线程间通信(主 Loop 唤醒 subLoop),通过回调函数实现业务层和网络层的解耦。
二、核心模块拆解:每个文件的作用与核心逻辑
代码中所有文件可分为基础工具、核心事件循环、网络通信、线程管理四大类,以下按依赖关系 + 功能重要性拆解核心模块,附关键文件对应关系。
(一)基础工具模块:通用能力封装,所有模块依赖
1. noncopyable.h/cpp:不可拷贝基类
- 核心作用:作为所有核心类的基类,禁止对象的拷贝构造和赋值操作(通过 C++11 的
= delete实现); - 使用场景:EventLoop、Poller、Channel、TcpServer 等核心类都继承自它,因为这些类是单例 / 线程绑定的,拷贝无意义且会导致错误;
- 设计亮点:构造 / 析构函数为
protected,保证派生类能正常构造析构,基类无法实例化。
2. Logger.h/cpp:日志系统
核心作用:提供分级日志(INFO/ERROR/FATAL/DEBUG),支持可变参数格式化输出,是调试和问题排查的核心;
核心实现:
- 单例模式:
Logger::instance()保证全局只有一个日志对象; - 宏封装:
LOG_INFO/LOG_ERROR等宏封装了日志级别设置、格式化、输出逻辑,简化调用; - 时间戳:结合 Timestamp 类,日志输出带时间,便于问题追溯;
- 单例模式:
使用场景:所有模块的错误处理、状态打印(如连接建立 / 关闭、事件触发、系统调用失败)。
3. Timestamp.h/cpp:时间戳工具
- 核心作用:封装系统时间,提供秒级时间戳和格式化字符串输出(年 / 月 / 日 时:分: 秒);
- 核心方法:
Timestamp::now()获取当前时间,toString()格式化输出; - 使用场景:日志输出、IO 事件触发时间记录(如 Channel 处理读事件的时间)。
4. CurrentThread.h/cpp:当前线程工具
核心作用:封装线程 ID(tid) 的获取和缓存,提升 tid 获取效率;
核心实现:
- 用
thread_local关键字定义线程局部变量t_cachedTid,每个线程有独立副本,避免多线程竞争; - 通过 Linux 系统调用
syscall(SYS_gettid)获取真实 tid(pthread_self () 获取的是线程句柄,非系统 tid);
- 用
使用场景:EventLoop 绑定线程、Thread 类记录线程 tid、日志打印线程信息。
(二)核心事件循环模块:Reactor 模型的核心,整个库的 “心脏”
1. EventLoop.h/cpp:事件循环类(Reactor 核心)
核心定位:每个线程唯一的事件循环,是 Reactor 模型的事件循环器,负责监听事件、处理事件、执行回调;
核心属性:
poller_:IO 复用器(EPollPoller),负责监听 fd 事件;wakeupFd_:线程间通信的唤醒 fd,用于其他线程唤醒当前 EventLoop;activeChannels_:Poller 上报的活跃事件 fd 对应的 Channel;pendingFunctors_:待执行的回调函数队列,用于跨线程执行回调;threadId_:当前 EventLoop 绑定的线程 tid,保证one loop per thread;
核心方法:
loop():开启事件循环,死循环执行Poller 监听事件→处理活跃 Channel→执行待处理回调;quit():退出事件循环,支持跨线程调用(通过 wakeupFd_唤醒);runInLoop/queueInLoop:执行回调,若在当前线程直接执行,否则加入队列并唤醒 EventLoop;updateChannel/removeChannel:委托 Poller 添加 / 删除 / 修改 Channel 的监听事件;
设计亮点:
- 用
__thread EventLoop *t_loopInThisThread保证每个线程只有一个 EventLoop; - 用
wakeupFd_(eventfd 实现)实现高效的线程间唤醒,非阻塞且轻量; - 待执行回调队列加锁保护,保证线程安全。
- 用
2. Poller.h/cpp + EPollPoller.h/cpp + DefaultPoller.cc:IO 复用器
核心定位:Reactor 模型的事件分离器,封装 Linux 的 epoll(可扩展 poll),负责监听多个 fd 的 IO 事件并上报给 EventLoop;
类关系:
Poller:抽象基类,定义 IO 复用的统一接口(poll/updateChannel/removeChannel);EPollPoller:Poller 的 epoll 实现,是实际工作的类;DefaultPoller.cc:通过环境变量选择 Poller 实现(默认 epoll),实现策略模式;
Poller 核心属性:
channels_:fd 到 Channel 的映射(unordered_map<int, Channel*>),管理所有监听的 Channel;ownerLoop_:所属的 EventLoop,保证 Poller 和 EventLoop 一一绑定;
EPollPoller 核心实现:
epollfd_:epoll 的文件描述符,通过epoll_create1创建;events_:epoll_event 数组,存储 epoll_wait 返回的活跃事件;poll():调用epoll_wait监听事件,将活跃事件封装为 Channel 并上报给 EventLoop;updateChannel():根据 Channel 的状态(新增 / 修改 / 删除)调用epoll_ctl(EPOLL_CTL_ADD/MOD/DEL);
设计亮点:
- 抽象基类 Poller 屏蔽了不同 IO 复用的差异,上层模块无需关心底层是 epoll 还是 poll;
- Channel 的
index_属性标记状态(kNew/kAdded/kDeleted),避免 epoll_ctl 的无效调用。
3. Channel.h/cpp:通道类(fd 的封装)
核心定位:Reactor 模型的事件处理器,封装了 fd 和其对应的事件(如 EPOLLIN/EPOLLOUT),是 fd 和 EventLoop/Poller 之间的桥梁;
核心属性:
fd_:封装的文件描述符(可以是 listenfd/connfd/wakeupfd);events_:fd感兴趣的事件(由用户设置,如 EPOLLIN);revents_:fd实际发生的事件(由 Poller 上报);loop_:所属的 EventLoop,保证 Channel 和 EventLoop 一一绑定;readCallback_/writeCallback_/closeCallback_/errorCallback_:fd 事件的回调函数,由上层模块设置;
核心方法:
enableReading/enableWriting/disableAll:设置 fd 感兴趣的事件,最终调用update()委托 Poller 更新;handleEvent():Poller 上报事件后,Channel 根据revents_调用对应的回调函数;tie():绑定智能指针,防止 Channel 处理事件时,对应的 TcpConnection 被销毁(生存期管理);
设计亮点:
- 对 fd 的事件进行封装和解耦,上层模块无需直接操作 epoll 和 fd,只需操作 Channel;
- 回调函数采用
std::function封装,支持任意可调用对象,灵活适配上层业务; tie()结合弱智能指针std::weak_ptr,实现 Channel 和 TcpConnection 的安全绑定。
(三)网络通信模块:实现 TCP 的连接建立、数据传输、连接关闭
1. InetAddress.h/cpp:socket 地址封装
核心作用:封装 Linux 的
sockaddr_in(IPv4),提供IP / 端口的格式化、转换、获取能力;核心方法:
- 构造函数:支持端口 + IP 初始化,自动设置
sockaddr_in的字段; toIp()/toIpPort()/toPort():将二进制的 socket 地址转换为字符串格式的 IP/IP: 端口 / 端口;getSockAddr()/setSockAddr():获取 / 设置底层的sockaddr_in;
- 构造函数:支持端口 + IP 初始化,自动设置
使用场景:Acceptor 监听地址、TcpConnection 的本地 / 对端地址、Socket 的绑定 / 连接操作。
2. Socket.h/cpp:socket 文件描述符封装
核心作用:封装 Linux 的socket 系统调用,负责socket 的创建、绑定、监听、接受连接、设置选项等底层操作;
核心属性:
sockfd_:封装的 socket fd,const 修饰,保证一生不变;核心方法:
bindAddress():绑定 InetAddress(封装bind系统调用);listen():监听连接(封装listen系统调用);accept():接受新连接(封装accept4,直接创建非阻塞 + 关闭执行时继承的 fd,适配 Reactor 非阻塞 IO);setTcpNoDelay/setReuseAddr/setReusePort:设置 TCP 选项,优化网络通信;shutdownWrite():关闭写端(封装shutdown,优雅关闭 TCP 连接);
设计亮点:
- 封装 socket 底层操作,上层模块无需关心系统调用的细节和错误处理;
accept4直接创建非阻塞 fd,避免后续手动设置,提升效率;- 析构函数自动关闭 fd,避免资源泄漏。
3. Acceptor.h/cpp:连接接受器
核心定位:运行在mainLoop,负责监听 listenfd 的读事件(新连接) 并接受连接,是主 Reactor 的核心组件;
核心属性:
acceptSocket_:封装的 listenfd Socket;acceptChannel_:listenfd 对应的 Channel,监听 EPOLLIN 事件;newConnectionCallback_:新连接建立后的回调函数(由 TcpServer 设置,实际是TcpServer::newConnection);
核心方法:
listen():开启监听,将 acceptChannel_加入 mainLoop 的 Poller,监听 EPOLLIN 事件;handleRead():listenfd 有新连接时的回调,调用Socket::accept()接受连接,然后执行newConnectionCallback_将 connfd 交给 TcpServer;
设计亮点:
- 专注于连接接受,职责单一,符合单一职责原则;
- 封装
listenfd的所有操作,mainLoop只需调用Acceptor::listen ()即可,无需关心细节。
4. Buffer.h/cpp:网络缓冲区
核心定位:封装字节缓冲区,解决 TCP粘包 / 拆包问题,是 TcpConnection 的数据收发缓冲区;
核心实现:基于
std::vector<char>实现,采用双指针(readerIndex_/writerIndex_)设计,分为预读区、可读区、可写区;kCheapPrepend(8 字节):预读区,用于插入头部数据,无需移动原有数据;readerIndex_:可读区起始位置,writerIndex_:可写区起始位置;- 可读字节数:
writerIndex_ - readerIndex_,可写字节数:buffer_.size() - writerIndex_;
核心方法:
readFd():从 fd 读取数据到缓冲区(封装readv,分散读,解决缓冲区不足问题);writeFd():将缓冲区数据写入 fd(封装write);append():向缓冲区写入数据,自动扩容;retrieve():从缓冲区读取数据后,移动 readerIndex_,实现浅删除(无需真正删除数据,提升效率);retrieveAllAsString():将可读区数据转为 std::string,并重设指针;
设计亮点:
- 双指针设计实现高效的缓冲区操作,浅删除避免内存拷贝;
readv分散读,结合栈上的 extrabuf(64K),解决缓冲区不足时的大数据读取;- 自动扩容,上层模块无需关心缓冲区大小,简化开发。
5. TcpConnection.h/cpp:TCP 连接类
核心定位:封装一个 TCP 连接的生命周期,负责connfd 的读写、事件处理、数据收发,是从 Reactor 处理 IO 事件的核心;
核心属性:
socket_:封装的 connfd Socket;channel_:connfd 对应的 Channel,监听 EPOLLIN/EPOLLOUT 事件;inputBuffer_/outputBuffer_:收 / 发数据缓冲区,解决粘包 / 拆包;state_:连接状态(kDisconnected/kConnecting/kConnected/kDisconnecting),原子变量保证线程安全;- 各种回调函数:
connectionCallback_(连接建立 / 关闭)、messageCallback_(收到数据)、writeCompleteCallback_(数据发送完成)等;
核心方法:
- 构造函数:初始化 Socket、Channel,设置 Channel 的读 / 写 / 关闭 / 错误回调;
connectEstablished():连接建立时的初始化,将 Channel 加入 subLoop 的 Poller,监听 EPOLLIN 事件,执行连接建立回调;connectDestroyed():连接销毁时的清理,将 Channel 从 Poller 中删除,执行连接关闭回调;handleRead():connfd 读事件回调,从 fd 读取数据到 inputBuffer_,执行 messageCallback_将数据交给上层业务;handleWrite():connfd 写事件回调,将 outputBuffer_的数据写入 fd,数据发送完成后关闭 EPOLLOUT 事件;send():发送数据,若在当前线程直接调用 sendInLoop,否则加入 subLoop 的回调队列;sendInLoop():实际的发送逻辑,若内核发送缓冲区有空间直接写,否则将数据加入 outputBuffer_并监听 EPOLLOUT 事件;shutdown():优雅关闭 TCP 连接,等待 outputBuffer_数据发送完成后关闭写端;
设计亮点:
- 继承
std::enable_shared_from_this,通过shared_from_this()获取自身智能指针,保证对象生存期(避免回调时对象被销毁); - 用原子变量
state_管理连接状态,保证多线程下的状态安全; - 输出缓冲区解决应用写快、内核发慢的问题,避免阻塞;
- 各种回调函数实现网络层和业务层的解耦,业务层只需设置回调即可处理数据。
- 继承
6. TcpServer.h/cpp:TCP 服务器类
核心定位:对外的TCP 服务器入口类,封装了mainLoop、Acceptor、EventLoopThreadPool,是上层业务使用网络库的唯一入口,屏蔽了所有底层细节;
核心属性:
acceptor_:连接接受器,运行在 mainLoop;threadPool_:事件循环线程池,管理 subLoop 和工作线程;connections_:TCP 连接的映射(unordered_map<std::string, TcpConnectionPtr>),管理所有已建立的连接;- 各种回调函数:由上层业务设置,最终传递给 TcpConnection;
核心方法:
- 构造函数:初始化 mainLoop、Acceptor,设置 Acceptor 的新连接回调(TcpServer::newConnection);
setThreadNum():设置工作线程数,即 subLoop 的数量;start():启动服务器,启动线程池,让 Acceptor 开始监听;newConnection():新连接建立后的回调,创建 TcpConnection 对象,将 connfd 分配给 subLoop,初始化 TcpConnection 的回调;removeConnection():连接关闭后的回调,从 connections_中删除 TcpConnection,执行连接销毁逻辑;
设计亮点:
- 高内聚低耦合:封装了所有底层模块,上层业务只需创建 TcpServer、设置回调、调用 start () 即可启动服务器;
- 用智能指针管理 TcpConnection,自动管理连接的生存期,避免资源泄漏;
- 轮询算法分配 subLoop,保证工作线程的负载均衡。
(四)线程管理模块:实现多线程和 one loop per thread
1. Thread.h/cpp:线程封装
核心作用:封装 C++11 的
std::thread,提供线程的创建、启动、等待能力,记录线程的 tid 和名称;核心属性:
thread_:std::thread 的智能指针,管理线程对象;tid_:线程的 tid;func_:线程执行的函数(std::function<void ()>);numCreated_:静态原子变量,记录创建的线程数,用于生成默认线程名;
核心方法:
start():启动线程,执行 func_,并通过信号量(sem_t)保证 tid 的正确获取;join():等待线程结束;- 析构函数:若线程已启动且未等待,将线程设置为分离态(detach),避免资源泄漏;
设计亮点:
- 封装
std::thread的细节,上层模块无需关心线程的创建和管理; - 用信号量保证 tid 的获取顺序,避免主线程提前获取未初始化的 tid;
- 静态原子变量记录线程数,自动生成默认线程名,便于调试。
- 封装
2. EventLoopThread.h/cpp:事件循环线程
核心定位:将EventLoop 和 Thread 绑定,实现一个线程一个 EventLoop,是线程池的基本单元;
核心属性:
thread_:封装的工作线程,线程执行函数为 threadFunc ();loop_:工作线程中创建的 EventLoop 对象;cond_/mutex_:条件变量和互斥锁,实现主线程和工作线程的同步(等待 loop_初始化完成);callback_:ThreadInitCallback,线程初始化完成后的回调;
核心方法:
startLoop():启动工作线程,等待 loop_初始化完成后返回 loop_的指针;threadFunc():工作线程的执行函数,创建 EventLoop 对象,执行初始化回调,然后调用loop->loop()开启事件循环;
设计亮点:
- 实现了EventLoop 和 Thread 的强绑定,保证 one loop per thread;
- 用条件变量实现主线程和工作线程的同步,确保主线程获取到的 loop_是已初始化完成的。
3. EventLoopThreadPool.h/cpp:事件循环线程池
核心定位:管理多个 EventLoopThread,提供subLoop 的创建、管理、分配能力,是主 Reactor 和从 Reactor 之间的桥梁;
核心属性:
baseLoop_:mainLoop(主线程的 EventLoop);threads_:EventLoopThread 的智能指针数组,管理所有工作线程;loops_:subLoop 的指针数组,存储所有工作线程的 EventLoop;next_:下一个分配的 subLoop 索引,用于轮询算法;
核心方法:
setThreadNum():设置工作线程数;start():创建指定数量的 EventLoopThread,启动工作线程,将 subLoop 加入 loops_;getNextLoop():轮询算法获取下一个 subLoop,实现连接的负载均衡;
设计亮点:
- 轮询算法分配 subLoop,简单高效,保证工作线程的负载均衡;
- 封装了线程池的所有细节,TcpServer 只需调用 setThreadNum 和 start 即可,无需关心线程的创建和管理;
- 支持单线程模式(numThreads_=0),此时所有操作都在 mainLoop 中执行,适配简单场景。
三、核心运行流程:从服务器启动到客户端通信的全链路
以客户端发起 TCP 连接→发送数据→服务器接收数据→关闭连接为例,拆解全链路的运行逻辑,让各模块的交互清晰可见。
步骤 1:服务器启动(TcpServer::start ())
上层业务创建TcpServer 对象,传入 mainLoop、监听地址、服务器名称,设置连接回调、消息回调;
调用
TcpServer::setThreadNum(n),设置工作线程数为 n(n>0 则为多线程模式,n=0 为单线程模式);调用
TcpServer::start(),启动服务器:- 启动EventLoopThreadPool,创建 n 个 EventLoopThread,每个工作线程创建一个 subLoop 并开启事件循环;
- 调用
Acceptor::listen(),mainLoop 的 Poller 开始监听listenfd的 EPOLLIN 事件(新连接)。
步骤 2:客户端发起连接,主 Reactor 接受连接
- 客户端调用
connect()发起 TCP 连接,服务器的listenfd触发 EPOLLIN 事件; - mainLoop 的 Poller 上报事件,触发
Acceptor::handleRead(); Acceptor::handleRead()调用Socket::accept()接受连接,得到connfd(非阻塞)和客户端地址;- 调用 TcpServer 的newConnection 回调,将 connfd 交给 TcpServer 处理。
步骤 3:TcpServer 分配连接给从 Reactor
TcpServer::newConnection()通过轮询算法(getNextLoop())从线程池中获取一个subLoop;- 创建TcpConnection 对象,封装 connfd、本地地址、客户端地址,将 TcpConnection 加入
connections_管理; - 设置 TcpConnection 的各种回调(连接 / 消息 / 关闭),这些回调是上层业务设置给 TcpServer 的;
- 调用
TcpConnection::connectEstablished(),并将该操作加入 subLoop 的回调队列,唤醒 subLoop;
步骤 4:从 Reactor 初始化连接,监听 connfd 事件
subLoop 被唤醒后,执行
TcpConnection::connectEstablished():- 将 connfd 对应的Channel加入 subLoop 的 Poller,监听EPOLLIN 事件(读事件);
- 设置 Channel 的读 / 写 / 关闭 / 错误回调(对应 TcpConnection 的 handleRead/handleWrite 等);
- 执行连接建立回调,通知上层业务有新连接建立。
subLoop 继续执行事件循环,Poller 开始监听 connfd 的 EPOLLIN 事件。
步骤 5:客户端发送数据,从 Reactor 处理读事件
- 客户端调用
send()发送数据,服务器的connfd触发 EPOLLIN 事件; - subLoop 的 Poller 上报事件,触发
Channel::handleEvent(); Channel::handleEvent()调用TcpConnection::handleRead();TcpConnection::handleRead()调用Buffer::readFd(),从 connfd 读取数据到**inputBuffer_**(自动处理粘包 / 拆包);- 调用消息回调,将 TcpConnection 指针和 inputBuffer_交给上层业务,上层业务从 inputBuffer_中读取数据并处理。
步骤 6:服务器发送数据,从 Reactor 处理写事件
上层业务调用
TcpConnection::send()发送数据;TcpConnection::send()判断是否在 subLoop 线程,若是直接调用sendInLoop,否则加入 subLoop 的回调队列并唤醒;TcpConnection::sendInLoop():- 若内核发送缓冲区有空间,直接调用
write()将数据写入 connfd; - 若数据未发送完,将剩余数据加入outputBuffer_,并让 Channel 监听EPOLLOUT 事件(写事件);
- 若内核发送缓冲区有空间,直接调用
当内核发送缓冲区有空间时,connfd 触发 EPOLLOUT 事件,触发
TcpConnection::handleWrite();handleWrite()调用Buffer::writeFd(),将 outputBuffer_的数据写入 connfd,数据发送完成后:- 从 Poller 中取消监听 EPOLLOUT 事件(避免频繁触发);
- 执行写完成回调,通知上层业务数据发送完成;
- 若连接处于关闭状态(kDisconnecting),调用
shutdownInLoop()优雅关闭连接。
步骤 7:客户端 / 服务器关闭连接,从 Reactor 处理关闭事件
情况 1:客户端主动关闭连接
客户端调用
close(),服务器的 connfd 触发EPOLLHUP 事件(读端关闭);subLoop 的 Poller 上报事件,触发
Channel::handleEvent(),调用TcpConnection::handleClose();TcpConnection::handleClose():- 将连接状态设置为 kDisconnected;
- 从 Poller 中删除 Channel,取消所有事件监听;
- 执行连接关闭回调,通知上层业务连接关闭;
- 调用 TcpServer 的removeConnection 回调,将 TcpConnection 从 connections_中删除。
情况 2:服务器主动关闭连接
- 上层业务调用
TcpConnection::shutdown(); TcpConnection::shutdown()将连接状态设置为 kDisconnecting,若 outputBuffer_无数据,直接调用socket_->shutdownWrite()关闭写端;- 若 outputBuffer_有数据,等待数据发送完成后,在
handleWrite()中调用shutdownInLoop()关闭写端; - 客户端收到 FIN 后,关闭读端并发送 FIN,服务器 connfd 触发 EPOLLHUP 事件,后续流程同客户端主动关闭。
步骤 8:连接资源清理
TcpServer::removeConnection()将 TcpConnection 从connections_中删除;- TcpConnection 的智能指针引用计数归 0,析构函数被调用,自动释放 Socket、Channel 等资源;
- connfd 被自动关闭,TCP 连接的生命周期结束。
四、关键设计思想与技术亮点
这份手写 muduo 库的核心价值不仅是实现了 TCP 通信,更重要的是采用了工业级的设计思想和技术选型,保证了代码的高并发、高可维护、高可扩展,以下是核心设计亮点:
1. 主从 Reactor 架构:解耦连接监听和数据处理
- 主 Reactor(mainLoop)只做连接监听,轻量无阻塞,保证新连接能被快速接受;
- 从 Reactor(subLoop)处理数据读写,多线程并行处理,充分利用多核 CPU;
- 连接通过轮询算法分配,实现负载均衡,避免单线程瓶颈。
2. one loop per thread:单线程无锁,高效安全
- 每个线程绑定一个 EventLoop,线程内所有操作都是单线程串行执行的,无需加锁,避免多线程竞争和死锁;
- 线程间通信通过wakeupfd实现,轻量高效,非阻塞;
- 用
thread_local线程局部变量保证每个线程只有一个 EventLoop,避免重复创建。
3. 基于 Channel 的 fd 封装:解耦 fd 和事件处理
- Channel 是 fd 的抽象,封装了 fd 的事件注册、事件处理、回调管理,上层模块无需直接操作 fd 和 epoll;
- Channel 和 EventLoop/Poller 一一绑定,保证事件处理的正确性;
- 回调函数采用
std::function封装,支持任意可调用对象,灵活适配上层业务。
4. 智能指针的生存期管理:避免野指针和资源泄漏
- 核心类(TcpConnection、EventLoopThread)采用智能指针管理,自动释放资源,避免内存泄漏;
- TcpConnection 继承
std::enable_shared_from_this,通过shared_from_this()获取自身智能指针,保证回调时对象不被销毁; - Channel 通过
tie()绑定 TcpConnection 的弱智能指针,实现安全的生存期绑定,避免悬垂指针。
5. 双指针缓冲区:高效解决 TCP 粘包 / 拆包
- 基于
std::vector<char>的双指针缓冲区,实现高效的字节操作,浅删除避免内存拷贝; readv分散读结合栈上缓冲区,解决大数据读取时的缓冲区不足问题;- 自动扩容,上层模块无需关心缓冲区大小,简化开发。
6. 非阻塞 IO + 事件驱动:高并发的基础
- 所有 fd(listenfd/connfd/wakeupfd)都设置为非阻塞,避免系统调用阻塞线程;
- 基于 epoll 的水平触发(LT) 实现事件驱动,保证事件不丢失,简化开发(相比边缘触发 ET,LT 更易维护);
- 只有当 fd 有事件时才会被处理,无事件时线程阻塞在 epoll_wait,减少 CPU 占用。
7. 回调机制:解耦网络层和业务层
- 所有核心事件(连接建立 / 关闭、数据到达、数据发送完成)都通过回调函数通知上层业务;
- 网络层只负责网络通信,业务层只负责业务处理,两者完全解耦,便于业务开发和网络库的维护;
- 回调函数支持任意可调用对象(函数、函数指针、lambda、绑定对象),灵活性高。
8. 模块化设计:单一职责,高内聚低耦合
- 每个模块只做一件事(如 Acceptor 只做连接接受,Poller 只做 IO 复用,TcpConnection 只做 TCP 连接管理),符合单一职责原则;
- 模块之间通过接口和回调交互,依赖抽象而非具体实现,符合依赖倒置原则;
- 上层模块屏蔽底层细节(如 TcpServer 屏蔽了 EventLoop、Poller、Channel 等所有底层模块),便于上层业务使用。
五、总结
这份手写 muduo 网络库是Reactor 模型和one loop per thread设计思想的经典实现,核心是通过主从 Reactor 架构实现高并发,通过单线程无锁实现高效安全,通过模块化和回调机制实现高可维护和高可扩展。
整个库的核心逻辑可以用一句话概括:主线程监听新连接,分配给工作线程,工作线程的事件循环监听连接的 IO 事件,通过 Channel 封装 fd 和事件,通过 Buffer 处理数据,通过回调函数将事件通知给上层业务。