目录
[一、前置类型定义与 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,负责已建立连接的读写事件
- 关键注意事项 :
- 必须在
start()之前调用setThreadNum()、setConnectionCallback()等设置接口- 强烈建议开启
kReusePort选项,避免服务器重启时 "地址已被占用"- 新连接会通过轮询(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 所属的线程中执行
- 关键注意事项 :
- 绝对不要在回调函数中执行耗时操作,会阻塞整个事件循环
- 跨线程操作必须通过
runInLoop()或queueInLoop()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 线程中执行
- 关键注意事项 :
- 不要手动创建 TcpConnection 对象,由 TcpServer/TcpClient 内部管理
send()是线程安全的,可以在任意线程调用- 正常关闭优先使用
shutdown(),异常情况才用forceClose()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()接口- 关键注意事项 :
- 不要在连接未建立成功时调用
connection()->send(),会导致空指针崩溃- 推荐使用
CountDownLatch同步等待连接成功(如你之前的翻译客户端代码)- 客户端通常使用
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
类核心解释
- 作用:实现线程间的同步等待,解决异步操作的同步问题
- 典型使用场景 :
- 客户端等待连接成功
- 主线程等待所有子线程完成初始化
- 等待多个异步任务全部完成
- 核心设计:基于互斥锁 + 条件变量实现,是 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字节,方便在数据前添加协议头(如长度、类型)- 自动扩容:当可写空间不足时,自动扩展缓冲区大小
- 关键注意事项 :
- 必须调用
retrieve*()系列方法消费数据,否则缓冲区会累积数据- 不要手动修改
readerIndex_和writerIndex_,使用提供的接口操作prepend*()系列方法是构建带长度头协议的标准做法,非常高效
感谢阅读,本文如有错漏之处,烦请斧正。