【C++】muduo核心类

目录

[一、前置类型定义与 InetAddress 类](#一、前置类型定义与 InetAddress 类)

类核心解释

[二、TcpServer 类(TCP 服务器核心)](#二、TcpServer 类(TCP 服务器核心))

类核心解释

[三、EventLoop 类(事件循环核心)](#三、EventLoop 类(事件循环核心))

类核心解释

[四、TcpConnection 类(TCP 连接核心)](#四、TcpConnection 类(TCP 连接核心))

类核心解释

[五、TcpClient 类(TCP 客户端核心)](#五、TcpClient 类(TCP 客户端核心))

类核心解释

[六、CountDownLatch 类(同步工具)](#六、CountDownLatch 类(同步工具))

类核心解释

[七、Buffer 类(网络缓冲区核心)](#七、Buffer 类(网络缓冲区核心))

类核心解释


Doxygen 注释符号表,放在这方便后续阅读注释

标签名 作用 示例
@brief 用一句话简要概括函数 / 类 / 变量的核心功能 @brief 判断连接是否处于已连接状态
@param 解释函数参数的含义、取值范围等 @param ip IP地址字符串(如"127.0.0.1")
@param[in] 明确标注为输入参数(函数仅读取,不修改) @param[in] len 要读取的字节数
@param[out] 明确标注为输出参数(函数会修改其值,通常为指针 / 引用) @param[out] buf 输出缓冲区,用于存储读取的数据
@param[in,out] 明确标注为输入输出参数(函数既读取又修改) @param[in,out] ctx 上下文对象,用于传递状态
@return 解释函数返回值的含义、取值范围 @return true表示已连接,false表示已断开
@note 标注使用该接口时的重要注意事项 @note 线程安全,可以在任意线程调用
@warning 标注严重使用风险 (级别高于 @note @warning 非线程安全,不要同时在多个线程调用
@details 补充 @brief 未说明的详细信息(如实现原理、使用场景) @details 如果当前线程就是EventLoop线程,回调会立即同步执行;否则放入队列异步执行
@see 参考其他相关的函数 / 类 @see TcpServer::start()
@deprecated 标记该接口已废弃,不推荐使用 @deprecated 请使用新接口 newFunction() 替代
@code / @endcode 插入代码示例 @code int x = 10; @endcode
@author 标注作者信息 @author Chen Shuo
@date 标注创建 / 修改日期 @date 2025-04-02

一、前置类型定义与 InetAddress

cpp 复制代码
#include <string>
#include <functional>
#include <memory>
#include <vector>
#include <atomic>
#include <boost/any.hpp>

// muduo命名空间
namespace muduo
{
namespace net
{

// 前置声明
class TcpConnection;
class Buffer;
class Timestamp;

// 连接智能指针类型别名
typedef std::shared_ptr<TcpConnection> TcpConnectionPtr;

// 连接状态回调函数类型:参数为当前连接的智能指针
typedef std::function<void (const TcpConnectionPtr&)> ConnectionCallback;

// 消息回调函数类型:参数为【连接指针、数据缓冲区、消息到达时间戳】
typedef std::function<void (const TcpConnectionPtr&, Buffer*, Timestamp)> MessageCallback;

// 写完成回调函数类型
typedef std::function<void (const TcpConnectionPtr&)> WriteCompleteCallback;

/**
 * @brief 网络地址类,封装IP地址和端口号
 * @note 继承自copyable,支持值传递
 */
class InetAddress : public muduo::copyable
{
public:
    /**
     * @brief 构造函数
     * @param ip IP地址字符串(如"127.0.0.1")
     * @param port 端口号
     * @param ipv6 是否为IPv6地址,默认false(IPv4)
     */
    InetAddress(StringArg ip, uint16_t port, bool ipv6 = false);
};

} // namespace net
} // namespace muduo

类核心解释

  • 作用:统一封装 IPv4/IPv6 地址和端口号,屏蔽底层 sockaddr 的复杂细节
  • 核心特点:值语义(可拷贝、可赋值),是 muduo 中所有网络接口的地址参数类型
  • 常用扩展接口 (图中未列出但必用):
    • toIpPort():返回 "IP: 端口" 格式的字符串
    • toIp():仅返回 IP 地址字符串
    • port():返回端口号
    • InetAddress(uint16_t port, bool loopbackOnly = false):仅指定端口的构造函数(服务器监听用)

二、TcpServer 类(TCP 服务器核心)

cpp 复制代码
namespace muduo
{
namespace net
{

class EventLoop;

/**
 * @brief TCP服务器核心类
 * @note 不可拷贝,是muduo服务器端编程的入口
 */
class TcpServer : noncopyable
{
public:
    // 端口选项枚举
    enum Option
    {
        kNoReusePort,  // 不开启端口复用
        kReusePort     // 开启端口复用(推荐)
    };

    /**
     * @brief 构造函数
     * @param loop 主事件循环指针
     * @param listenAddr 监听的地址和端口
     * @param nameArg 服务器名称(用于日志区分多个实例)
     * @param option 端口选项,默认不开启端口复用
     */
    TcpServer(EventLoop* loop,
              const InetAddress& listenAddr,
              const std::string& nameArg,
              Option option = kNoReusePort);

    /**
     * @brief 设置IO线程池的线程数
     * @param numThreads IO线程数量,0表示所有IO都在主线程执行
     * @note 必须在start()之前调用
     */
    void setThreadNum(int numThreads);

    /**
     * @brief 启动服务器,开始监听端口
     * @note 非阻塞调用,真正的事件处理在EventLoop::loop()中
     */
    void start();

    /**
     * @brief 设置连接状态回调函数
     * @param cb 回调函数,新连接建立或旧连接断开时调用
     */
    void setConnectionCallback(const ConnectionCallback& cb)
    {
        connectionCallback_ = cb;
    }

    /**
     * @brief 设置消息回调函数
     * @param cb 回调函数,收到对端发送的数据时调用
     */
    void setMessageCallback(const MessageCallback& cb)
    {
        messageCallback_ = cb;
    }

private:
    EventLoop* loop_;                  // 主事件循环
    std::string name_;                 // 服务器名称
    ConnectionCallback connectionCallback_;  // 连接回调
    MessageCallback messageCallback_;        // 消息回调
    // 其他内部成员(省略)
};

} // namespace net
} // namespace muduo

类核心解释

  • 作用:TCP 服务器的总入口,负责监听端口、接受新连接、管理所有 TcpConnection 对象
  • 核心模型 :one loop per thread + 线程池
    • 主线程(主 EventLoop):只负责接受新连接
    • IO 线程池:每个线程一个 EventLoop,负责已建立连接的读写事件
  • 关键注意事项
    1. 必须在start()之前调用setThreadNum()setConnectionCallback()等设置接口
    2. 强烈建议开启kReusePort选项,避免服务器重启时 "地址已被占用"
    3. 新连接会通过轮询(Round-Robin)方式分配到不同的 IO 线程

三、EventLoop 类(事件循环核心)

cpp 复制代码
namespace muduo
{
namespace net
{

class Poller;
class TimerId;
class TimerCallback;

/**
 * @brief 事件循环类,muduo的核心心脏
 * @note 不可拷贝,一个线程只能有一个EventLoop实例
 */
class EventLoop : noncopyable
{
public:
    EventLoop();
    ~EventLoop();

    /**
     * @brief 启动事件循环,进入无限阻塞等待
     * @note 必须在创建该EventLoop的线程中调用
     */
    void loop();

    /**
     * @brief 退出事件循环
     * @note 线程安全,调用后会在处理完当前事件后退出
     */
    void quit();

    /**
     * @brief 在指定时间点执行一次回调
     * @param time 执行时间
     * @param cb 定时器回调函数
     * @return 定时器ID,可用于取消定时器
     */
    TimerId runAt(Timestamp time, TimerCallback cb);

    /**
     * @brief 延迟指定时间后执行一次回调
     * @param delay 延迟时间,单位:秒
     * @param cb 定时器回调函数
     * @return 定时器ID
     * @note 线程安全
     */
    TimerId runAfter(double delay, TimerCallback cb);

    /**
     * @brief 每隔指定时间周期性执行回调
     * @param interval 间隔时间,单位:秒
     * @param cb 定时器回调函数
     * @return 定时器ID
     * @note 线程安全
     */
    TimerId runEvery(double interval, TimerCallback cb);

    /**
     * @brief 取消指定的定时器
     * @param timerId 要取消的定时器ID
     * @note 线程安全
     */
    void cancel(TimerId timerId);

    /**
     * @brief 在EventLoop线程中执行任务
     * @param cb 要执行的回调函数
     * @note 如果当前线程就是EventLoop线程,回调会立即同步执行;否则放入队列异步执行
     */
    void runInLoop(Functor cb);

    /**
     * @brief 将任务放入队列,总是异步执行
     * @param cb 要执行的回调函数
     */
    void queueInLoop(Functor cb);

private:
    std::atomic<bool> quit_;                // 退出标志
    std::unique_ptr<Poller> poller_;        // IO多路复用器(epoll/poll)
    mutable MutexLock mutex_;               // 保护pendingFunctors_的互斥锁
    std::vector<Functor> pendingFunctors_ GUARDED_BY(mutex_);  // 待执行的异步任务队列
};

} // namespace net
} // namespace muduo

类核心解释

  • 作用:事件分发器,负责 IO 事件、定时器事件、异步任务的调度执行
  • 核心设计
    • 每个线程只能有一个 EventLoop,通过线程局部存储(TLS)实现
    • 内部使用 epoll(默认)作为 IO 多路复用器
    • 所有回调函数都在 EventLoop 所属的线程中执行
  • 关键注意事项
    1. 绝对不要在回调函数中执行耗时操作,会阻塞整个事件循环
    2. 跨线程操作必须通过runInLoop()queueInLoop()
    3. loop()是阻塞调用,会一直运行直到调用quit()

四、TcpConnection 类(TCP 连接核心)

cpp 复制代码
namespace muduo
{
namespace net
{

/**
 * @brief TCP连接类,代表一个已建立的TCP连接
 * @note 不可拷贝,生命周期由shared_ptr管理,继承自enable_shared_from_this
 */
class TcpConnection : noncopyable,
                      public std::enable_shared_from_this<TcpConnection>
{
public:
    /**
     * @brief 构造函数(用户不要手动创建,由TcpServer/TcpClient内部创建)
     * @param loop 所属的EventLoop
     * @param name 连接名称
     * @param sockfd 已连接的socket文件描述符
     * @param localAddr 本端地址
     * @param peerAddr 对端地址
     */
    TcpConnection(EventLoop* loop,
                  const std::string& name,
                  int sockfd,
                  const InetAddress& localAddr,
                  const InetAddress& peerAddr);

    /**
     * @brief 判断连接是否处于已连接状态
     * @return true表示已连接,false表示已断开
     */
    bool connected() const
    {
        return state_ == kConnected;
    }

    /**
     * @brief 判断连接是否处于已断开状态
     * @return true表示已断开
     */
    bool disconnected() const
    {
        return state_ == kDisconnected;
    }

    // 发送数据的多个重载版本
    void send(std::string&& message);           // C++11移动语义版本
    void send(const void* message, int len);    // 原始指针+长度版本
    void send(const StringPiece& message);      // StringPiece版本
    void send(Buffer* message);                 // Buffer版本(会交换数据,高效)

    /**
     * @brief 优雅关闭连接(半关闭)
     * @note 关闭写端,发送FIN包,仍可接收对端数据
     * @warning 非线程安全,不要同时在多个线程调用
     */
    void shutdown();

    /**
     * @brief 强制关闭连接
     * @note 直接发送RST包,立即断开连接
     */
    void forceClose();

    /**
     * @brief 设置连接上下文(用于存储自定义数据)
     * @param context 任意类型的上下文数据
     */
    void setContext(const boost::any& context)
    {
        context_ = context;
    }

    /**
     * @brief 获取连接上下文(只读)
     * @return 上下文数据
     */
    const boost::any& getContext() const
    {
        return context_;
    }

    /**
     * @brief 获取可修改的连接上下文
     * @return 上下文数据指针
     */
    boost::any* getMutableContext()
    {
        return &context_;
    }

    /**
     * @brief 获取所属的EventLoop指针
     * @return EventLoop指针
     */
    EventLoop* getLoop() const
    {
        return loop_;
    }

    /**
     * @brief 获取对端地址
     * @return 对端InetAddress
     */
    const InetAddress& peerAddress() const;

    /**
     * @brief 获取本端地址
     * @return 本端InetAddress
     */
    const InetAddress& localAddress() const;

    // 设置连接回调(一般由TcpServer/TcpClient内部设置)
    void setConnectionCallback(const ConnectionCallback& cb)
    {
        connectionCallback_ = cb;
    }

    // 设置消息回调(一般由TcpServer/TcpClient内部设置)
    void setMessageCallback(const MessageCallback& cb)
    {
        messageCallback_ = cb;
    }

    // 设置写完成回调
    void setWriteCompleteCallback(const WriteCompleteCallback& cb)
    {
        writeCompleteCallback_ = cb;
    }

private:
    // 连接状态枚举
    enum StateE
    {
        kDisconnected,  // 已断开
        kConnecting,    // 正在连接
        kConnected,     // 已连接
        kDisconnecting  // 正在断开
    };

    EventLoop* loop_;                          // 所属的EventLoop
    std::string name_;                         // 连接名称
    int sockfd_;                               // socket文件描述符
    InetAddress localAddr_;                    // 本端地址
    InetAddress peerAddr_;                     // 对端地址
    StateE state_;                             // 连接状态
    ConnectionCallback connectionCallback_;    // 连接回调
    MessageCallback messageCallback_;          // 消息回调
    WriteCompleteCallback writeCompleteCallback_;  // 写完成回调
    boost::any context_;                       // 用户自定义上下文
};

} // namespace net
} // namespace muduo

类核心解释

  • 作用:代表一个端到端的 TCP 连接,是所有网络数据收发的核心
  • 核心设计
    • 生命周期由shared_ptr管理,自动释放资源
    • 继承自enable_shared_from_this,可以安全地生成指向自身的 shared_ptr
    • 所有操作都必须在所属的 EventLoop 线程中执行
  • 关键注意事项
    1. 不要手动创建 TcpConnection 对象,由 TcpServer/TcpClient 内部管理
    2. send()是线程安全的,可以在任意线程调用
    3. 正常关闭优先使用shutdown(),异常情况才用forceClose()
    4. boost::any context_是非常实用的功能,可以存储任意类型的连接相关数据(如用户信息、会话状态)

五、TcpClient 类(TCP 客户端核心)

cpp 复制代码
namespace muduo
{
namespace net
{

/**
 * @brief TCP客户端核心类
 * @note 不可拷贝,是muduo客户端编程的入口
 */
class TcpClient : noncopyable
{
public:
    /**
     * @brief 构造函数
     * @param loop 客户端的事件循环
     * @param serverAddr 服务器的地址和端口
     * @param nameArg 客户端名称(用于日志)
     */
    TcpClient(EventLoop* loop,
              const InetAddress& serverAddr,
              const std::string& nameArg);

    ~TcpClient();

    /**
     * @brief 异步发起连接
     * @note 非阻塞调用,连接成功/失败通过onConnection回调通知
     */
    void connect();

    /**
     * @brief 断开连接
     */
    void disconnect();

    /**
     * @brief 停止客户端
     */
    void stop();

    /**
     * @brief 获取当前连接的智能指针
     * @return TcpConnectionPtr,可能为空(连接未建立时)
     * @note 线程安全
     */
    TcpConnectionPtr connection() const
    {
        MutexLockGuard lock(mutex_);
        return connection_;
    }

    /**
     * @brief 设置连接状态回调函数
     * @param cb 回调函数
     */
    void setConnectionCallback(ConnectionCallback cb)
    {
        connectionCallback_ = std::move(cb);
    }

    /**
     * @brief 设置消息回调函数
     * @param cb 回调函数
     */
    void setMessageCallback(MessageCallback cb)
    {
        messageCallback_ = std::move(cb);
    }

    /**
     * @brief 设置写完成回调函数
     * @param cb 回调函数
     */
    void setWriteCompleteCallback(WriteCompleteCallback cb)
    {
        writeCompleteCallback_ = std::move(cb);
    }

private:
    EventLoop* loop_;                          // 事件循环
    std::string name_;                         // 客户端名称
    InetAddress serverAddr_;                   // 服务器地址
    ConnectionCallback connectionCallback_;    // 连接回调
    MessageCallback messageCallback_;          // 消息回调
    WriteCompleteCallback writeCompleteCallback_;  // 写完成回调
    mutable MutexLock mutex_;                  // 保护connection_的互斥锁
    TcpConnectionPtr connection_ GUARDED_BY(mutex_);  // 当前连接指针
};

} // namespace net
} // namespace muduo

类核心解释

  • 作用:TCP 客户端的总入口,负责发起异步连接、管理客户端的 TcpConnection 对象
  • 核心特点
    • 连接是异步的,connect()调用立即返回
    • 自动重连(可配置)
    • 线程安全的connection()接口
  • 关键注意事项
    1. 不要在连接未建立成功时调用connection()->send(),会导致空指针崩溃
    2. 推荐使用CountDownLatch同步等待连接成功(如你之前的翻译客户端代码)
    3. 客户端通常使用EventLoopThread单独启动一个 IO 线程,避免阻塞主线程

六、CountDownLatch 类(同步工具)

cpp 复制代码
namespace muduo
{

/**
 * @brief 倒计时门闩同步工具类
 * @note 不可拷贝,用于线程间的等待-通知机制
 */
class CountDownLatch : noncopyable
{
public:
    /**
     * @brief 构造函数
     * @param count 初始计数
     */
    explicit CountDownLatch(int count);

    /**
     * @brief 等待计数变为0
     * @note 阻塞当前线程,直到count_减到0
     */
    void wait()
    {
        MutexLockGuard lock(mutex_);
        while (count_ > 0)
        {
            condition_.wait();
        }
    }

    /**
     * @brief 计数减1
     * @note 当计数减到0时,唤醒所有等待的线程
     */
    void countDown()
    {
        MutexLockGuard lock(mutex_);
        --count_;
        if (count_ == 0)
        {
            condition_.notifyAll();
        }
    }

    /**
     * @brief 获取当前计数
     * @return 当前计数
     */
    int getCount() const;

private:
    mutable MutexLock mutex_;       // 互斥锁
    Condition condition_ GUARDED_BY(mutex_);  // 条件变量
    int count_ GUARDED_BY(mutex_);  // 计数
};

} // namespace muduo

类核心解释

  • 作用:实现线程间的同步等待,解决异步操作的同步问题
  • 典型使用场景
    1. 客户端等待连接成功
    2. 主线程等待所有子线程完成初始化
    3. 等待多个异步任务全部完成
  • 核心设计:基于互斥锁 + 条件变量实现,是 muduo 中最常用的同步工具

七、Buffer 类(网络缓冲区核心)

cpp 复制代码
namespace muduo
{
namespace net
{

/**
 * @brief 网络数据缓冲区类,解决TCP粘包/拆包问题
 * @note 继承自copyable,采用双指针设计(读指针+写指针)
 */
class Buffer : public muduo::copyable
{
public:
    static const size_t kCheapPrepend = 8;    // 前置预留空间大小(用于添加协议头)
    static const size_t kInitialSize = 1024;  // 初始缓冲区大小

    /**
     * @brief 构造函数
     * @param initialSize 初始缓冲区大小,默认1024字节
     */
    explicit Buffer(size_t initialSize = kInitialSize)
        : buffer_(kCheapPrepend + initialSize),
          readerIndex_(kCheapPrepend),
          writerIndex_(kCheapPrepend)
    {}

    /**
     * @brief 交换两个Buffer的内容
     * @param rhs 要交换的Buffer
     */
    void swap(Buffer& rhs);

    /**
     * @brief 获取可读字节数
     * @return 可读字节数
     */
    size_t readableBytes() const;

    /**
     * @brief 获取可写字节数
     * @return 可写字节数
     */
    size_t writableBytes() const;

    /**
     * @brief 获取可读数据的起始指针(不移动读指针)
     * @return 可读数据指针
     */
    const char* peek() const;

    /**
     * @brief 查找换行符'\n'
     * @return 找到的换行符指针,未找到返回nullptr
     */
    const char* findEOL() const;
    const char* findEOL(const char* start) const;

    // 消费指定长度的数据(移动读指针)
    void retrieve(size_t len);
    void retrieveInt64();
    void retrieveInt32();
    void retrieveInt16();
    void retrieveInt8();

    /**
     * @brief 取出所有可读数据并转为字符串,同时清空缓冲区
     * @return 数据字符串
     */
    std::string retrieveAllAsString();

    /**
     * @brief 取出指定长度的可读数据并转为字符串
     * @param len 要取出的长度
     * @return 数据字符串
     */
    std::string retrieveAsString(size_t len);

    // 追加数据到缓冲区
    void append(const StringPiece& str);
    void append(const char* data, size_t len);
    void append(const void* data, size_t len);

    // 获取可写区域的起始指针
    char* beginWrite();
    const char* beginWrite() const;

    /**
     * @brief 标记已写入指定长度的数据(移动写指针)
     * @param len 已写入的长度
     */
    void hasWritten(size_t len);

    // 追加整数类型数据(网络字节序)
    void appendInt64(int64_t x);
    void appendInt32(int32_t x);
    void appendInt16(int16_t x);
    void appendInt8(int8_t x);

    // 读取整数类型数据(网络字节序转主机字节序)
    int64_t readInt64();
    int32_t readInt32();
    int16_t readInt16();
    int8_t readInt8();

    // 查看整数类型数据(不移动读指针)
    int64_t peekInt64() const;
    int32_t peekInt32() const;
    int16_t peekInt16() const;
    int8_t peekInt8() const;

    // 在可读区域前追加数据(用于添加协议头)
    void prependInt64(int64_t x);
    void prependInt32(int32_t x);
    void prependInt16(int16_t x);
    void prependInt8(int8_t x);
    void prepend(const void* data, size_t len);

private:
    std::vector<char> buffer_;    // 底层存储
    size_t readerIndex_;          // 读指针
    size_t writerIndex_;          // 写指针
    static const char kCRLF[];    // "\r\n"常量
};

} // namespace net
} // namespace muduo

类核心解释

  • 作用:muduo 解决 TCP 粘包 / 拆包问题的核心,自动管理数据的读写
  • 核心设计
    • 双指针模型:readerIndex_(读位置)和writerIndex_(写位置)
    • 前置预留空间:kCheapPrepend=8字节,方便在数据前添加协议头(如长度、类型)
    • 自动扩容:当可写空间不足时,自动扩展缓冲区大小
  • 关键注意事项
    1. 必须调用retrieve*()系列方法消费数据,否则缓冲区会累积数据
    2. 不要手动修改readerIndex_writerIndex_,使用提供的接口操作
    3. prepend*()系列方法是构建带长度头协议的标准做法,非常高效

感谢阅读,本文如有错漏之处,烦请斧正。

相关推荐
剑穗挂着新流苏3122 小时前
208_深度学习的鲁棒性之美:暂退法(Dropout)原理与实战
开发语言·pytorch·python·深度学习
小草cys2 小时前
review202604032342
开发语言·php
一只小阿乐2 小时前
js流式模式输出 函数模式使用
开发语言·javascript·ai·vue·agent·流式数据·node 服务
伯远医学2 小时前
如何判断提取的RNA是否可用?
java·开发语言·前端·javascript·人工智能·eclipse·创业创新
搜狐技术产品小编20232 小时前
端侧Python动态算法策略的部署与运行
开发语言·python
cch89182 小时前
C++与PHP:7大核心差异全解析
java·开发语言
ID_180079054732 小时前
Python 采集转转二手商品详情:API 接口与爬虫实战全方案(2026 最新)
开发语言·爬虫·python
lifewange2 小时前
JavaScript是什么
开发语言·javascript·ecmascript
环黄金线HHJX.2 小时前
《Tuan(拼音字母)⇆团(Group)/&湍(Turbulence)/&双结构链路道/&文字、符号、语言/&源点设计、连接起:人类与自然+AICosmOS》
开发语言·人工智能·算法·编辑器