Muduo:(5) 主 Reactor 之 Acceptor 与 SubReactor 的分发

1. 模块概述

1.1 Acceptor

Acceptor 是 mymuduo 网络库中负责接受新连接的组件,它封装了监听 socket 的创建、绑定、监听和接受连接等操作。Acceptor 的核心设计目标包括:

  1. 监听 socket 管理:创建和管理监听 socket
  2. 新连接接受:接受客户端连接并获取对端地址
  3. 回调通知:通过回调机制通知上层有新连接到达
  4. Socket 选项配置:支持地址重用、端口重用等选项
cpp 复制代码
+--------------------------------------------------+
|                 主线程 Acceptor                   |
+--------------------------------------------------+
|                                                   |
|  +-------------+     +-------------+              |
|  |   Socket    |     |   Channel   |              |
|  | (listen fd) |     | (监听读事件) |              |
|  +-------------+     +-------------+              |
|                                                   |
|  +------------------------------------------+    |
|  |         NewConnectionCallback            |    |
|  |  void(int sockfd, const InetAddress&)    |    |
|  +------------------------------------------+    |
|                                                   |
+--------------------------------------------------+
组件 角色 线程归属 职责
Acceptor MainReactor 主线程(单个) 1. 监听端口 2. accept() 接收新连接 3. 分发连接给 SubReactor
EventLoopThreadPool SubReactor 池 工作线程(多个) 1. 处理已建立连接的 I/O 事件 2. 执行业务逻辑

1.2 TcpConnection

TcpConnection 是 mymuduo 网络库中代表 TCP 连接的核心类,它封装了一个已建立的 TCP 连接的所有状态和操作。TcpConnection 的核心设计目标包括:

  1. 连接状态管理:管理连接的完整生命周期状态
  2. 数据收发:提供线程安全的数据发送接口
  3. 事件处理:处理 socket 的读写关闭错误事件
  4. 回调机制:为用户提供丰富的事件回调接口
  5. 生命周期管理:通过 shared_ptr 实现安全的对象生命周期

1.3 TcpServer

TcpServer 是 mymuduo 网络库的核心服务器类,它封装了 TCP 服务器的完整生命周期管理。TcpServer 的核心设计目标包括:

  1. 连接管理:管理所有客户端连接的创建、维护和销毁

  2. 线程池管理:支持多线程处理,实现 Reactor 线程池模式

  3. 回调管理:提供丰富的回调接口供用户自定义业务逻辑

  4. 资源管理:自动管理服务器资源,确保正确释放

    +--------------------------------------------------+
    | TcpServer |
    +--------------------------------------------------+
    | |
    | +-------------+ +------------------------+ |
    | | Acceptor | | EventLoopThreadPool | |
    | | (mainLoop) | | (subLoops) | |
    | +------+------+ +-----------+------------+ |
    | | | |
    | v +-----------+-----------+ |
    | 新连接到达 | | | | | |
    | | v v v v | |
    | | Loop0 Loop1 Loop2 Loop3 | |
    | | | | | | | |
    | +---------->+-----+-----+-----+-----+ |
    | 分发新连接到 subLoop |
    | |
    +--------------------------------------------------+

2. 源码

2.1 Acceptor.h

cpp 复制代码
#pragma once
#include "NonCopyable.h"  // 不可拷贝基类
#include "Socket.h"       // Socket类
#include "Channel.h"      // Channel类
#include <functional>     // 函数对象
namespace mymuduo {
class EventLoop;
class InetAddress;
/**
 * 
 * ================================================
 *              ** 外部模块调用关系 **
 * ================================================
 * 
 * | 调用方模块          | 调用的Acceptor方法            | 用途                             |
 * |---------------------|-------------------------------|----------------------------------|
 * | TcpServer           | Acceptor() 构造函数          | 创建监听器                       |
 * | TcpServer           | setNewConnectionCallback()    | 设置新连接回调                   |
 * | TcpServer           | listen()                      | 启动监听                         |
 * | EventLoop           | handleRead() (内部事件触发)   | 处理新连接事件                   |
 * 
 * ================================================
 */
class Acceptor : NonCopyable  // 继承自不可拷贝基类
{
public:
    /**
     * @brief 新连接到达时的回调函数类型
     * @param sockfd 新连接的socket文件描述符
     * @param addr 客户端地址信息
     */
    using NewConnectionCallback = std::function<void(int sockfd, const InetAddress&)>;

    /**
     * @brief 构造函数
     * @param loop 所属的事件循环
     * @param listenAddr 要监听的地址
     * @param reuseport 是否启用端口重用
     */
    Acceptor(EventLoop *loop, const InetAddress &listenAddr, bool reuseport);
    ~Acceptor(); // 析构函数
    
    /**
     * @brief 设置新连接回调函数
     * @param cb 回调函数对象
     */
    void setNewConnectionCallback(const NewConnectionCallback &cb) { 
        NewConnectionCallback_ = std::move(cb); 
    }
    
    /**
     * @brief 获取当前是否正在监听
     * @return true表示正在监听,false表示未监听
     */
    bool listenning() const {return listenning_;}
    void listen(); // 开始监听连接
private:
    void handleRead(); //处理可读事件(新连接到达)

    EventLoop *loop_;           // 所属的事件循环
    Socket acceptSocket_;       // 监听socket
    Channel acceptChannel_;     // 监听socket对应的channel
    NewConnectionCallback NewConnectionCallback_; // 新连接回调函数,根据轮询选择一个subloop唤醒,把当前connfd封装成channel发给它
    bool listenning_;           // 是否正在监听的标志
};
} // namespace mymuduo

2.2 Acceptor.cpp

cpp 复制代码
#include "Acceptor.h"
#include "LogStream.h"
#include "InetAddress.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>
using namespace mymuduo;

/**
 * @brief 创建非阻塞的socket文件描述符
 * @return 成功返回socket文件描述符,失败退出程序
 */
static int createNonblocking() {
    // 创建非阻塞且close-on-exec的TCP socket
    int sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);
    if (sockfd < 0) {
        LOG_ERROR << "listen socket create err: " << errno;
        exit(-1);
    }
    return sockfd;
}

/**
 * @brief 构造函数
 * @param loop 所属的事件循环
 * @param listenAddr 监听的地址
 * @param reuseport 是否启用端口重用
 */
Acceptor::Acceptor(EventLoop *loop, const InetAddress &listenAddr, bool reuseport)
    : loop_(loop)                          // 所属事件循环
    , acceptSocket_(createNonblocking())   // 创建监听socket
    , acceptChannel_(loop, acceptSocket_.fd()) // 把socketfd打包为一个channel给mianloop的poller
    , listenning_(false)                   // 初始状态未开始监听
{
    acceptSocket_.setReuseAddr(true);      // 启用地址重用
    acceptSocket_.setReusePort(true);      // 启用端口重用
    acceptSocket_.bindAddress(listenAddr); // 绑定监听地址
    
    // 设置channel的读事件回调,只关心读事件,即新连接
    acceptChannel_.setReadCallback(std::bind(&Acceptor::handleRead, this));
}

Acceptor::~Acceptor() {
    // 禁用所有事件并从事件循环中移除channel
    acceptChannel_.disableAll();
    acceptChannel_.remove();
}

/**
 * @brief 开始监听连接
 */
void Acceptor::listen() {
    listenning_ = true;            // 标记为正在监听状态
    acceptSocket_.listen();        // 开始监听socket连接
    acceptChannel_.enableReading();// 启用channel的读事件监听
}

/**
 * @brief 处理新连接到达的读事件
 */
void Acceptor::handleRead() {
    InetAddress peerAddr;          // 用于保存客户端地址
    
    // 接受新连接
    int connfd = acceptSocket_.accept(&peerAddr);
    
    if (connfd >= 0) {
        // 如果有新连接回调函数,则调用
        if (NewConnectionCallback_) {
            NewConnectionCallback_(connfd, peerAddr);
        } else {
            // 否则直接关闭连接
            ::close(connfd);
        }
    } else {
        LOG_ERROR << "accept err: " << errno;
        
        // 特殊处理文件描述符耗尽的情况
        if (errno == EMFILE) LOG_ERROR << "sockfd reached limit";
    }
}

2.3 TcpConnection.h

cpp 复制代码
#pragma once
#include "NonCopyable.h"     // 禁止拷贝的基类
#include "InetAddress.h"     // 网络地址封装
#include "Callbacks.h"       // 回调函数定义
#include "Buffer.h"         // 数据缓冲区
#include "Timestamp.h"       // 时间戳
#include <memory>
#include <string>
#include <atomic>

namespace mymuduo {

class Channel;   // 事件通道(封装fd和事件回调)
class EventLoop; // 事件循环
class Socket;    // 套接字封装

/**
 * ================================================
 *              ** 外部模块调用关系 **
 * ================================================
 * 
 * | 调用方模块          | 调用的TcpConnection方法       | 用途                             |
 * |---------------------|-------------------------------|----------------------------------|
 * | TcpServer           | connectEstablished()          | 通知连接已建立                   |
 * | TcpServer           | connectDestroyed()            | 通知连接已销毁                   |
 * | 用户代码            | send()/sendFile()             | 发送数据                         |
 * | 用户代码            | shutdown()                    | 关闭连接                         |
 * | 用户代码            | setXXXCallback()系列方法      | 设置各类事件回调                 |
 * | EventLoop           | handleRead()/handleWrite()等  | 处理底层socket事件               |
 * 
 * ================================================
 */
class TcpConnection : NonCopyable,  // 禁止拷贝
                     public std::enable_shared_from_this<TcpConnection> { // 支持shared_from_this
public:
    /**
     * @brief 构造函数
     * @param loop 所属事件循环(必须是IO线程)
     * @param nameArg 连接名称(用于日志标识)
     * @param sockfd 已连接的套接字
     * @param localAddr 本地地址
     * @param peerAddr 对端地址
     */
    TcpConnection(EventLoop *loop, 
                 const std::string &nameArg, 
                 int sockfd,
                 const InetAddress &localAddr,
                 const InetAddress &peerAddr);
    
    ~TcpConnection() {};

    // 获取基本信息
    EventLoop* getLoop() const { return loop_; }
    const std::string& name() const { return name_; }
    const InetAddress& localAddress() const { return localAddr_; }
    const InetAddress& peerAddress() const { return peerAddr_; }

    // 连接状态检查
    bool connected() const { return state_ == kConnected; }

    // 数据发送接口
    void send(const std::string &buf);   // 发送字符串数据
    void sendFile(int fileDescriptor,    // 发送文件(零拷贝)
                 off_t offset, 
                 size_t count); 
    
    // 关闭连接(半关闭)
    void shutdown();

    // 设置各类回调函数
    void setConnectionCallback(const ConnectionCallback &cb) 
    { connectionCallback_ = cb; }
    
    void setMessageCallback(const MessageCallback &cb) 
    { messageCallback_ = cb; }
    
    void setWriteCompleteCallback(const WriteCompleteCallback &cb) 
    { writeCompleteCallback_ = cb; }
    
    void setCloseCallback(const CloseCallback &cb) 
    { closeCallback_ = cb; }
    
    void setHighWaterMarkCallback(const HighWaterMarkCallback &cb, size_t highWaterMark) 
    { highWaterMarkCallback_ = cb; highWaterMark_ = highWaterMark; }

    // 连接管理
    void connectEstablished();  // 在IO线程中调用,通知连接已建立
    void connectDestroyed();    // 在IO线程中调用,通知连接已销毁
private:
    // 连接状态枚举
    enum StateE {
        kDisconnected,  // 已断开
        kConnecting,    // 正在连接(未使用)
        kConnected,     // 已连接
        kDisconnecting  // 正在断开
    };

    void setState(StateE state) { state_ = state; }

    // Channel事件回调
    void handleRead(Timestamp receiveTime);  // 处理可读事件
    void handleWrite();                      // 处理可写事件
    void handleClose();                      // 处理关闭事件
    void handleError();                      // 处理错误事件

    // 线程安全的内部实现
    void sendInLoop(const void *data, size_t len);
    void shutdownInLoop();
    void sendFileInLoop(int fileDescriptor, off_t offset, size_t count);

private:
    EventLoop *loop_;        // 所属事件循环(必须是IO线程)
    const std::string name_; // 连接名称(用于日志和调试)
    std::atomic_int state_;  // 连接状态(原子操作,线程安全)
    bool reading_;           // 是否正在监听可读事件

    // 核心组件
    std::unique_ptr<Socket> socket_;   // 套接字对象(RAII管理fd)
    std::unique_ptr<Channel> channel_; // 事件通道

    // 地址信息
    const InetAddress localAddr_;  // 本地地址
    const InetAddress peerAddr_;   // 对端地址

    // 用户回调函数
    ConnectionCallback connectionCallback_;        // 连接建立/关闭回调
    MessageCallback messageCallback_;              // 消息到达回调
    WriteCompleteCallback writeCompleteCallback_;  // 数据发送完成回调
    HighWaterMarkCallback highWaterMarkCallback_;  // 高水位回调(流量控制)
    CloseCallback closeCallback_;                  // 连接关闭回调
    size_t highWaterMark_;                         // 高水位阈值

    // 数据缓冲区
    Buffer inputBuffer_;   // 输入缓冲区(存放接收的数据)
    Buffer outputBuffer_;  // 输出缓冲区(存放待发送的数据)
};
} // namespace mymuduo

2.4 TcpConnection.cpp

cpp 复制代码
#include "TcpConnection.h"
#include "Socket.h"
#include "Channel.h"
#include "EventLoop.h"
#include "LogStream.h"
#include <sys/sendfile.h>  // 零拷贝发送文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace mymuduo;

// 辅助函数:检查EventLoop是否有效
static EventLoop* CheckLoopNotNull(EventLoop* loop) {
    if (loop == nullptr) {
        LOG_ERROR << "mainLoop is null!!!";
        exit(-1);
    }
    return loop;
}

/**
 * @brief TcpConnection构造函数
 * @param loop 所属事件循环(必须非空)
 * @param nameArg 连接名称标识
 * @param sockfd 已建立的TCP套接字
 * @param localAddr 本地地址信息
 * @param peerAddr 对端地址信息
 */
TcpConnection::TcpConnection(EventLoop* loop,
                             const std::string& nameArg,
                             int sockfd,
                             const InetAddress& localAddr,
                             const InetAddress& peerAddr)
    : loop_(CheckLoopNotNull(loop)), // 校验loop有效性
      name_(nameArg),
      state_(kConnecting),           // 初始状态为连接中
      reading_(true),                // 默认开启读监听
      socket_(new Socket(sockfd)),   // 接管套接字所有权
      channel_(new Channel(loop, sockfd)),  // 创建事件通道
      localAddr_(localAddr),
      peerAddr_(peerAddr),
      highWaterMark_(64 * 1024 * 1024) {   // 默认高水位64MB

    // 设置Channel事件回调(绑定到当前对象成员函数)
    channel_->setReadCallback(std::bind(&TcpConnection::handleRead, this, std::placeholders::_1));
    channel_->setWriteCallback(std::bind(&TcpConnection::handleWrite, this));
    channel_->setCloseCallback(std::bind(&TcpConnection::handleClose, this));
    channel_->setErrorCallback(std::bind(&TcpConnection::handleError, this));

    LOG_INFO << "TcpConnection::ctor[" << name_ << "] at fd=" << sockfd;
    socket_->setKeepAlive(true);  // 开启TCP保活机制
}

/********************* 数据发送相关方法 *********************/
/**
 * @brief 发送字符串数据(线程安全)
 * @param buf 待发送数据
 * 原理:跨线程调用通过runInLoop转发到IO线程执行
 */
void TcpConnection::send(const std::string& buf) {
    if (state_ == kConnected) {  // 仅连接状态下允许发送
        if (loop_->isInLoopThread()) {
            sendInLoop(buf.c_str(), buf.size());  // 直接执行
        } else {
            // 跨线程时通过事件队列转发
            loop_->runInLoop( std::bind(&TcpConnection::sendInLoop, this, buf.c_str(), buf.size()));
        }
    }
}

/**
 * @brief 在IO线程中执行实际发送操作
 * @param data 数据指针
 * @param len 数据长度
 * 策略:优先尝试直接发送,失败则存入输出缓冲区并监听可写事件
 */
void TcpConnection::sendInLoop(const void* data, size_t len) {
    ssize_t nwrote = 0;
    size_t remaining = len;
    bool faultError = false;

    // 状态检查
    if (state_ == kDisconnected) {
        LOG_ERROR << "disconnected, give up writing";
        return;
    }

    // Case1: 当前无待发送数据且通道未注册写事件
    if (!channel_->isWriting() && outputBuffer_.readableBytes() == 0) {
        nwrote = ::write(channel_->fd(), data, len);  // 尝试直接发送
        if (nwrote >= 0) {
            remaining = len - nwrote;
            // 全部发送完成且设置了写完成回调
            if (remaining == 0 && writeCompleteCallback_) {
                loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));
            }
        } else {  // 发送出错处理
            nwrote = 0;
            if (errno != EWOULDBLOCK) {  // 非预期错误
                LOG_ERROR << "TcpConnection::sendInLoop";
                if (errno == EPIPE || errno == ECONNRESET) {  // 连接异常
                    faultError = true;
                }
            }
        }
    }

    // Case2: 仍有剩余数据待发送或发生非致命错误
    if (!faultError && remaining > 0) {
        size_t oldLen = outputBuffer_.readableBytes();
        // 高水位检测(流量控制)
        if (oldLen + remaining >= highWaterMark_ && oldLen < highWaterMark_ && highWaterMarkCallback_) {
            loop_->queueInLoop(std::bind(highWaterMarkCallback_, shared_from_this(), oldLen + remaining));
        }
        // 剩余数据存入输出缓冲区
        outputBuffer_.append(static_cast<const char*>(data) + nwrote, remaining);
        if (!channel_->isWriting()) {
            channel_->enableWriting();  // 注册可写事件监听
        }
    }
}

/********************* 连接管理方法 *********************/
/**
 * @brief 关闭连接(半关闭,线程安全)
 * 原理:通过shutdown关闭写端,保留读端直到对端关闭
 */
void TcpConnection::shutdown() {
    if (state_ == kConnected) {
        setState(kDisconnecting);  // 状态转移
        loop_->runInLoop(          // 确保在IO线程执行
            std::bind(&TcpConnection::shutdownInLoop, this));
    }
}

/**
 * @brief 实际执行关闭操作(必须在IO线程)
 * 条件:输出缓冲区数据已全部发送完成
 */
void TcpConnection::shutdownInLoop() {
    if (!channel_->isWriting()) {  // 确保数据已发送完毕
        socket_->shutdownWrite();  // 关闭写端
    }
}

/********************* 连接生命周期方法 *********************/
/**
 * @brief 通知连接已建立(由TcpServer调用)
 * 流程:1. 设置状态 2. 绑定shared_ptr 3. 注册读事件 4. 回调用户
 */
void TcpConnection::connectEstablished() {
    setState(kConnected);
    channel_->tie(shared_from_this());  // 防止Channel回调时对象被销毁
    channel_->enableReading();          // 注册读事件监听
    
    // 新连接建立,执行回调
    connectionCallback_(shared_from_this());
}

/**
 * @brief 通知连接已销毁(由TcpServer调用)
 * 流程:1. 取消所有事件监听 2. 触发回调 3. 从Poller移除Channel
 */
void TcpConnection::connectDestroyed() {
    if (state_ == kConnected) {
        setState(kDisconnected);
        channel_->disableAll();  // 取消所有事件监听
        connectionCallback_(shared_from_this());
    }
    channel_->remove();  // 从Poller中移除
}

/********************* 事件回调处理 *********************/
/**
 * @brief 处理可读事件
 * @param receiveTime 数据到达时间戳
 * 流程:1. 读取数据到输入缓冲区 2. 触发消息回调
 */
void TcpConnection::handleRead(Timestamp receiveTime) {
    int savedErrno = 0;
    ssize_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno);
    if (n > 0) {
        // 触发用户消息到达回调(数据存放在inputBuffer_)
        messageCallback_(shared_from_this(), &inputBuffer_, receiveTime);
    } else if (n == 0) {  // 对端关闭连接
        handleClose();
    } else {  // 读取错误
        errno = savedErrno;
        LOG_ERROR << "TcpConnection::handleRead";
        handleError();
    }
}

/**
 * @brief 处理可写事件
 * 流程:1. 发送输出缓冲区数据 2. 根据发送情况调整监听状态
 */
void TcpConnection::handleWrite() {
    if (channel_->isWriting()) {
        int savedErrno = 0;
        ssize_t n = outputBuffer_.writeFd(channel_->fd(), &savedErrno);
        if (n > 0) {
            outputBuffer_.retrieve(n);  // 移动读指针
            // 缓冲区数据已全部发送完成
            if (outputBuffer_.readableBytes() == 0) {
                channel_->disableWriting();  // 取消写事件监听
                if (writeCompleteCallback_) {
                    loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));
                }
                // 如果正在关闭连接,执行最终关闭
                if (state_ == kDisconnecting) shutdownInLoop();
            }
        } else {
            LOG_ERROR << "TcpConnection::handleWrite";
        }
    } else {
        LOG_ERROR << "Connection fd = " << channel_->fd() << " is down, no more writing";
    }
}

/**
 * @brief 处理连接关闭
 * 流程:1. 更新状态 2. 取消事件监听 3. 触发用户回调
 */
void TcpConnection::handleClose() {
    LOG_INFO << "fd = " << channel_->fd() << " state = " << state_;
    setState(kDisconnected);
    channel_->disableAll();  // 取消所有事件监听
    TcpConnectionPtr connPtr(shared_from_this());
    connectionCallback_(connPtr);  // 连接状态变更回调
    closeCallback_(connPtr);       // 关闭回调(最终会触发TcpServer移除连接)
}

/**
 * @brief 处理错误事件
 * 流程:获取套接字错误码并记录日志
 */
void TcpConnection::handleError() {
    int optval;
    socklen_t optlen = sizeof(optval);
    int err = 0;
    if (::getsockopt(channel_->fd(), SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0) {
        err = errno;
    } else {
        err = optval;
    }
    LOG_ERROR << "TcpConnection::handleError [" << name_ 
             << "] - SO_ERROR = " << err;
}

/********************* 零拷贝文件发送 *********************/
/**
 * @brief 发送文件(线程安全)
 * @param fileDescriptor 文件描述符
 * @param offset 起始偏移量
 * @param count 发送字节数
 * 原理:使用sendfile系统调用实现内核级零拷贝
 */
void TcpConnection::sendFile(int fileDescriptor, off_t offset, size_t count) {
    if (connected()) {
        if (loop_->isInLoopThread()) {
            sendFileInLoop(fileDescriptor, offset, count);
        } else {
            loop_->runInLoop(
                std::bind(&TcpConnection::sendFileInLoop,
                         shared_from_this(),
                         fileDescriptor,
                         offset,
                         count));
        }
    } else {
        LOG_ERROR << "TcpConnection::sendFile - not connected";
    }
}

/**
 * @brief 实际执行文件发送(必须在IO线程)
 * 策略:优先使用sendfile,失败则转入异步重试
 */
void TcpConnection::sendFileInLoop(int fileDescriptor, off_t offset, size_t count) {
    ssize_t bytesSent = 0;
    size_t remaining = count;
    bool faultError = false;

    if (state_ == kDisconnecting) {
        LOG_ERROR << "disconnected, give up writing";
        return;
    }

    // Case1: 当前无待发送数据
    if (!channel_->isWriting() && outputBuffer_.readableBytes() == 0) {
        bytesSent = ::sendfile(socket_->fd(), fileDescriptor, &offset, remaining);
        if (bytesSent >= 0) {
            remaining -= bytesSent;
            if (remaining == 0 && writeCompleteCallback_) {
                loop_->queueInLoop(
                    std::bind(writeCompleteCallback_, shared_from_this()));
            }
        } else {
            if (errno != EWOULDBLOCK) {
                LOG_ERROR << "TcpConnection::sendFileInLoop";
                if (errno == EPIPE || errno == ECONNRESET) {
                    faultError = true;
                }
            }
        }
    }

    // Case2: 处理剩余数据或错误
    if (!faultError && remaining > 0) {
        loop_->queueInLoop(
            std::bind(&TcpConnection::sendFileInLoop,
                     shared_from_this(),
                     fileDescriptor,
                     offset,
                     remaining));
    }
}

2.5 TcpServer.h

cpp 复制代码
#pragma once 
#include "EventLoop.h"          // 事件循环类
#include "Acceptor.h"           // 接收器类,处理新连接
#include "InetAddress.h"        // 网络地址封装
#include "NonCopyable.h"        // 不可拷贝基类
#include "EventLoopThreadPool.h" // 事件循环线程池
#include "Callbacks.h"          // 回调函数定义
#include "TcpConnection.h"      // TCP连接类
#include "Buffer.h"             // 缓冲区类
#include <functional>           // 函数对象
#include <string>               // 字符串
#include <memory>               // 智能指针
#include <atomic>               // 原子操作
#include <unordered_map>        // 哈希表
namespace mymuduo {
/**
 * ================================================
 *              ** 外部模块调用关系 **
 * ================================================
 * 
 * | 调用方模块          | 调用的TcpServer方法              | 用途                             |
 * |---------------------|----------------------------------|----------------------------------|
 * | 主程序              | TcpServer() 构造函数            | 创建服务器实例                   |
 * | 主程序              | setThreadNum()                  | 设置工作线程数量                 |
 * | 主程序              | setXXXCallback() 系列方法       | 设置各类事件回调                 |
 * | 主程序              | start()                         | 启动服务器                       |
 * | Acceptor            | newConnection() (内部调用)      | 处理新连接                       |
 * | TcpConnection       | removeConnection() (内部调用)   | 移除断开连接                     |
 * | EventLoopThreadPool | threadInitCallback_ (回调)      | 线程池初始化时执行自定义逻辑     |
 * 
 * ================================================
 */
class TcpServer : NonCopyable  // 继承自不可拷贝基类
{
public:
    using ThreadInitCallback = std::function<void(EventLoop*)>;  // 线程初始化回调类型

    /**
     * @brief 端口重用选项枚举
     */
    enum Option
    {
        kNoReusePort,  // 不重用端口
        kReusePort,    // 重用端口(SO_REUSEPORT)
    };

    /**
     * @brief 构造函数
     * @param loop 事件循环指针
     * @param listenAddr 监听地址
     * @param nameArg 服务器名称
     * @param option 端口重用选项,默认为不重用
     */
    TcpServer(EventLoop* loop,
              const InetAddress& listenAddr,
              const std::string& nameArg,
              Option option = kNoReusePort);

    ~TcpServer(); // 析构函数

    // 设置各种回调函数
    void setThreadInitCallback(const ThreadInitCallback& cb) { threadInitCallback_ = cb; }          // 设置线程初始化回调
    void setConnectionCallback(const ConnectionCallback& cb) { connectionCallback_ = cb; }          // 设置连接建立/关闭回调
    void setMessageCallback(const MessageCallback& cb) { messageCallback_ = cb; }                   // 设置消息到达回调
    void setWriteCompleteCallback(const WriteCompleteCallback& cb) { writeCompleteCallback_ = cb; } // 设置写完成回调

    /**
     * @brief 设置工作线程数量
     * @param numThreads 线程数量
     * 
     * @note 必须在start()前调用
     */
    void setThreadNum(int numThreads);
    void start(); // 启动服务器
private:
    using ConnectionMap = std::unordered_map<std::string, TcpConnectionPtr>;  // 连接映射表类型

    /**
     * @brief 新连接到达时的处理函数
     * @param sockfd 新连接的socket文件描述符
     * @param peerAddr 对端地址
     */
    void newConnection(int sockfd, const InetAddress& peerAddr);

    /**
     * @brief 移除连接
     * @param conn 要移除的连接指针
     */
    void removeConnection(const TcpConnectionPtr& conn);

    /**
     * @brief 在事件循环中移除连接
     * @param conn 要移除的连接指针
     */
    void removeConnectionInLoop(const TcpConnectionPtr& conn);

    EventLoop* loop_;  // 主事件循环指针

    const std::string ipPort_;  // 服务器监听的IP和端口字符串表示
    const std::string name_;    // 服务器名称

    std::unique_ptr<Acceptor> acceptor_;  // 接收器,负责接受新连接
    std::shared_ptr<EventLoopThreadPool> threadPool_;  // 事件循环线程池

    // 各种回调函数
    ConnectionCallback connectionCallback_;        // 连接建立/关闭回调
    MessageCallback messageCallback_;              // 消息到达回调
    WriteCompleteCallback writeCompleteCallback_;  // 写完成回调
    ThreadInitCallback threadInitCallback_;        // 线程初始化回调

    int numThreads_;             // 线程数量
    std::atomic_int started_;    // 服务器启动状态(原子变量)
    int nextConnId_;             // 下一个连接ID
    ConnectionMap connections_;  // 当前所有连接的映射表
};

}  // namespace mymuduo

2.6 TcpServer.cpp

cpp 复制代码
#include "TcpServer.h"
#include "LogStream.h"
#include "TcpConnection.h"
#include <functional>
#include <string.h>
using namespace mymuduo;

/**
 * @brief 检查事件循环指针是否有效
 * @param loop 要检查的事件循环指针
 * @return 有效的事件循环指针
 * 
 * @note 如果传入的指针为空,会记录错误日志并退出程序
 */
static EventLoop* CheckLoopNotNull(EventLoop* loop) {
    if (loop == nullptr) {
        LOG_ERROR << "mainLoop is null!!!";
        exit(-1);
    }
    return loop;
}

/**
 * @brief TcpServer构造函数
 * @param loop 主事件循环
 * @param listenAddr 服务器监听地址
 * @param nameArg 服务器名称
 * @param option 端口重用选项
 * 
 * 初始化服务器各个组件,设置Acceptor的新连接回调
 */
TcpServer::TcpServer(EventLoop* loop, 
                    const InetAddress& listenAddr, 
                    const std::string& nameArg, 
                    Option option)
    : loop_(CheckLoopNotNull(loop))
    , ipPort_(listenAddr.toIpPort())
    , name_(nameArg)
    , acceptor_(new Acceptor(loop, listenAddr, option == kReusePort))
    , threadPool_(new EventLoopThreadPool(loop, name_))
    , connectionCallback_()
    , messageCallback_()
    , writeCompleteCallback_()
    , threadInitCallback_()
    , nextConnId_(1)
    , started_(0)
{
    // 设置Acceptor的新连接回调,有新连接时调用newConnection处理
    acceptor_->setNewConnectionCallback(std::bind(&TcpServer::newConnection, this, std::placeholders::_1, std::placeholders::_2));
}

/**
 * @brief TcpServer析构函数
 * 
 * 清理所有连接,确保资源正确释放
 */
TcpServer::~TcpServer() {
    for (auto& item : connections_) {
        TcpConnectionPtr conn(item.second);
        item.second.reset();
        conn->getLoop()->runInLoop(std::bind(&TcpConnection::connectDestroyed, conn));
    }
}

/**
 * @brief 设置线程池中的线程数量
 * @param numThreads 要设置的工作线程数量
 * 
 * @note 必须在start()之前调用
 */
void TcpServer::setThreadNum(int numThreads) {
    threadPool_->setThreadNum(numThreads);
}

/**
 * @brief 启动服务器
 * 
 * 启动线程池并开始监听连接
 */
void TcpServer::start() {
    if (started_.fetch_add(1) == 0) { // 获得旧值并旧值加1
        // 启动线程池
        threadPool_->start(threadInitCallback_);
        // 在主事件循环中开始监听
        loop_->runInLoop(std::bind(&Acceptor::listen, acceptor_.get()));
    }
}

/**
 * @brief 处理新连接
 * @param sockfd 新连接的socket描述符
 * @param peerAddr 对端地址
 * 
 * 为新连接创建TcpConnection对象,设置各种回调并加入到连接管理
 */
void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr) {
    // 从线程池获取一个事件循环
    EventLoop* ioLoop = threadPool_->getNextLoop();
    
    // 生成连接名称
    char buf[64] = {0};
    snprintf(buf, sizeof buf, "-%s#%d", ipPort_.c_str(), nextConnId_);
    ++nextConnId_;
    std::string connName = name_ + buf;

    LOG_INFO << "TcpServer::newConnection [" << name_.c_str() 
             << "] - new connection [" << connName.c_str() 
             << "] from " << peerAddr.toIpPort().c_str();

    // 获取本地地址
    sockaddr_in local;
    ::memset(&local, 0, sizeof(local));
    socklen_t addrlen = sizeof(local);
    if (::getsockname(sockfd, (sockaddr*)&local, &addrlen) < 0) {
        LOG_ERROR << "sockets::getLocalAddr";
    }

    InetAddress localAddr(local);
    
    // 创建新的TcpConnection对象
    TcpConnectionPtr conn(new TcpConnection(ioLoop, connName, sockfd, localAddr, peerAddr));
    
    // 将连接加入管理
    connections_[connName] = conn;
    
    // 设置各种回调
    conn->setConnectionCallback(connectionCallback_);
    conn->setMessageCallback(messageCallback_);
    conn->setWriteCompleteCallback(writeCompleteCallback_);
    conn->setCloseCallback(std::bind(&TcpServer::removeConnection, this, std::placeholders::_1));
    
    // 在IO线程中建立连接
    ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));
}

/**
 * @brief 移除连接
 * @param conn 要移除的连接
 * 
 * 将连接移除操作调度到主事件循环中执行
 */
void TcpServer::removeConnection(const TcpConnectionPtr& conn) {
    loop_->runInLoop(std::bind(&TcpServer::removeConnectionInLoop, this, conn));
}

/**
 * @brief 在事件循环中移除连接
 * @param conn 要移除的连接
 * 
 * 实际执行连接移除操作,并从连接管理中删除
 */
void TcpServer::removeConnectionInLoop(const TcpConnectionPtr& conn) {
    LOG_INFO << "TcpServer::removeConnectionInLoop [" << name_.c_str() << "] - connection " << conn->name().c_str();
    
    // 从连接管理中移除
    connections_.erase(conn->name());
    
    // 在IO线程中销毁连接
    EventLoop* ioLoop = conn->getLoop();
    ioLoop->queueInLoop(std::bind(&TcpConnection::connectDestroyed, conn));
}

3. 模块交互

EventLoop 通过 Poller(epoll) 同时监听三类 fd

1️⃣ wakeupFd_ (所有 EventLoop 必备)→ 唤醒阻塞的 poll

2️⃣ listenfd (仅 MainReactor)→ 接收新连接

3️⃣ 多个 connfd(SubReactor)→ 处理业务数据

相关推荐
Bruce_Liuxiaowei2 小时前
从域名到IP:DNS解析过程与安全防护详解
网络协议·tcp/ip·安全
天上飞的粉红小猪2 小时前
数据链路层
linux·服务器·网络
Non importa2 小时前
二分法:算法新手第三道坎
c语言·c++·笔记·qt·学习·算法·leetcode
王老师青少年编程2 小时前
2020年信奥赛C++提高组csp-s初赛真题及答案解析(完善程序第1题)
c++·题解·真题·初赛·信奥赛·csp-s·提高组
开发者导航2 小时前
精选高质量网址资源的高效聚合综合性的网址导航:跳跳兔导航网
服务器·人工智能·程序人生·搜索引擎·开源软件
学编程的闹钟2 小时前
安装GmSSL3库后用VS编译CMake源码
c语言·c++·ide·开发工具·cmake·visual studio
2023自学中4 小时前
笔记本电脑 连接 手机WIFI,开发板网线连接笔记本,开发板 和 虚拟机 同时上网
linux·单片机·嵌入式硬件·tcp/ip
funnycoffee12310 小时前
linux系统DNS修改命令
linux·运维·服务器·linux dns
想放学的刺客10 小时前
整理了120道单片机嵌入式面试题与答案,覆盖了硬件电路和C语言等核心领域。
c语言·c++·stm32·单片机·嵌入式硬件·mcu·51单片机