源代码地址: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

架构核心思想

  1. 主线程只做最轻量化的连接监听,不处理任何业务和数据读写,避免被阻塞;
  2. 新连接建立后,通过轮询算法分配给某个工作线程的 subLoop,由 subLoop 全程负责该连接的读、写、关闭等所有 IO 事件;
  3. 每个线程绑定一个独立的 EventLoop(事件循环),线程内所有操作都是单线程无锁的,避免多线程竞争,提升效率;
  4. 通过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
  • 使用场景: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 ())

  1. 上层业务创建TcpServer 对象,传入 mainLoop、监听地址、服务器名称,设置连接回调、消息回调

  2. 调用TcpServer::setThreadNum(n),设置工作线程数为 n(n>0 则为多线程模式,n=0 为单线程模式);

  3. 调用TcpServer::start(),启动服务器:

    • 启动EventLoopThreadPool,创建 n 个 EventLoopThread,每个工作线程创建一个 subLoop 并开启事件循环;
    • 调用Acceptor::listen(),mainLoop 的 Poller 开始监听listenfd的 EPOLLIN 事件(新连接)。

步骤 2:客户端发起连接,主 Reactor 接受连接

  1. 客户端调用connect()发起 TCP 连接,服务器的listenfd触发 EPOLLIN 事件;
  2. mainLoop 的 Poller 上报事件,触发Acceptor::handleRead()
  3. Acceptor::handleRead()调用Socket::accept()接受连接,得到connfd(非阻塞)和客户端地址;
  4. 调用 TcpServer 的newConnection 回调,将 connfd 交给 TcpServer 处理。

步骤 3:TcpServer 分配连接给从 Reactor

  1. TcpServer::newConnection()通过轮询算法getNextLoop())从线程池中获取一个subLoop
  2. 创建TcpConnection 对象,封装 connfd、本地地址、客户端地址,将 TcpConnection 加入connections_管理;
  3. 设置 TcpConnection 的各种回调(连接 / 消息 / 关闭),这些回调是上层业务设置给 TcpServer 的;
  4. 调用TcpConnection::connectEstablished(),并将该操作加入 subLoop 的回调队列,唤醒 subLoop

步骤 4:从 Reactor 初始化连接,监听 connfd 事件

  1. subLoop 被唤醒后,执行TcpConnection::connectEstablished()

    • 将 connfd 对应的Channel加入 subLoop 的 Poller,监听EPOLLIN 事件(读事件);
    • 设置 Channel 的读 / 写 / 关闭 / 错误回调(对应 TcpConnection 的 handleRead/handleWrite 等);
    • 执行连接建立回调,通知上层业务有新连接建立。
  2. subLoop 继续执行事件循环,Poller 开始监听 connfd 的 EPOLLIN 事件。

步骤 5:客户端发送数据,从 Reactor 处理读事件

  1. 客户端调用send()发送数据,服务器的connfd触发 EPOLLIN 事件;
  2. subLoop 的 Poller 上报事件,触发Channel::handleEvent()
  3. Channel::handleEvent()调用TcpConnection::handleRead()
  4. TcpConnection::handleRead()调用Buffer::readFd(),从 connfd 读取数据到**inputBuffer_**(自动处理粘包 / 拆包);
  5. 调用消息回调,将 TcpConnection 指针和 inputBuffer_交给上层业务,上层业务从 inputBuffer_中读取数据并处理。

步骤 6:服务器发送数据,从 Reactor 处理写事件

  1. 上层业务调用TcpConnection::send()发送数据;

  2. TcpConnection::send()判断是否在 subLoop 线程,若是直接调用sendInLoop,否则加入 subLoop 的回调队列并唤醒;

  3. TcpConnection::sendInLoop()

    • 若内核发送缓冲区有空间,直接调用write()将数据写入 connfd;
    • 若数据未发送完,将剩余数据加入outputBuffer_,并让 Channel 监听EPOLLOUT 事件(写事件);
  4. 当内核发送缓冲区有空间时,connfd 触发 EPOLLOUT 事件,触发TcpConnection::handleWrite()

  5. handleWrite()调用Buffer::writeFd(),将 outputBuffer_的数据写入 connfd,数据发送完成后:

    • 从 Poller 中取消监听 EPOLLOUT 事件(避免频繁触发);
    • 执行写完成回调,通知上层业务数据发送完成;
    • 若连接处于关闭状态(kDisconnecting),调用shutdownInLoop()优雅关闭连接。

步骤 7:客户端 / 服务器关闭连接,从 Reactor 处理关闭事件

情况 1:客户端主动关闭连接

  1. 客户端调用close(),服务器的 connfd 触发EPOLLHUP 事件(读端关闭);

  2. subLoop 的 Poller 上报事件,触发Channel::handleEvent(),调用TcpConnection::handleClose()

  3. TcpConnection::handleClose()

    • 将连接状态设置为 kDisconnected;
    • 从 Poller 中删除 Channel,取消所有事件监听;
    • 执行连接关闭回调,通知上层业务连接关闭;
    • 调用 TcpServer 的removeConnection 回调,将 TcpConnection 从 connections_中删除。

情况 2:服务器主动关闭连接

  1. 上层业务调用TcpConnection::shutdown()
  2. TcpConnection::shutdown()将连接状态设置为 kDisconnecting,若 outputBuffer_无数据,直接调用socket_->shutdownWrite()关闭写端;
  3. 若 outputBuffer_有数据,等待数据发送完成后,在handleWrite()中调用shutdownInLoop()关闭写端;
  4. 客户端收到 FIN 后,关闭读端并发送 FIN,服务器 connfd 触发 EPOLLHUP 事件,后续流程同客户端主动关闭。

步骤 8:连接资源清理

  1. TcpServer::removeConnection()将 TcpConnection 从connections_中删除;
  2. TcpConnection 的智能指针引用计数归 0,析构函数被调用,自动释放 Socket、Channel 等资源;
  3. 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 处理数据,通过回调函数将事件通知给上层业务