游戏服务器应使用std::shared_ptr管理连接生命周期,避免裸指针导致的重复释放或访问已析构对象;epoll采用lt模式更适配不定长协议;网络与逻辑线程分离,用队列通信;unordered_map遍历时需防rehash失效。

用 std::shared_ptr 管理连接生命周期,别裸指针存 Socket
游戏服务器里最常崩的点,是连接断开时还在往已释放的 Socket 写数据。裸指针或原始 new 出来的对象,一不留神就被 close() 两次,或者异步回调里访问了已析构对象。
正确做法是让每个连接(比如 TcpConnection)自己持有对底层 Socket 的 std::shared_ptr<socket></socket>,同时把连接对象本身也用 shared_ptr 托管在 ConnectionManager 里。这样只要还有读写事件、定时器、业务逻辑在引用它,对象就不会销毁。
- 别在
onClose()回调里直接delete this—— 这和裸指针手动管理没区别 - 所有异步操作(如
async_write、async_wait)的 lambda 捕获列表必须含shared_from_this(),否则回调执行时对象可能已销毁 -
weak_ptr适合用在定时器或心跳检查里,避免循环引用:先lock()拿到shared_ptr,为空就跳过处理
用 epoll + LT 模式做网络层,别碰 ET 除非你重写了整个缓冲区逻辑
Linux 下高并发 IO 的事实标准是 epoll,但新手常掉进 ET(Edge Triggered)陷阱:以为它“更高效”,结果收不全包、卡住连接、CPU 空转。
LT 模式天然适配游戏协议的不定长包(如 TLV 或分隔符),一次 read() 不够就下次再读,不会丢事件;而 ET 要求你必须一次性把 socket 接收缓冲区读空,否则后续可读事件不触发 —— 这意味着你要自己实现循环读 + 边界判断 + 拆包缓存,出错概率翻倍。
- 用
epoll_ctl(..., EPOLL_CTL_ADD, ..., EPOLLET)就是主动踩坑,除非你有完整自研的零拷贝 ring buffer 和状态机拆包器 -
recv()返回0表示对端关闭,返回-1且errno == EAGAIN或EWOULDBLOCK才是真正“没数据了” - 每个连接配一个固定大小的接收缓冲区(如 4KB),用
std::vector<char></char>管理,避免频繁 new/delete
业务逻辑和网络线程分离,用 std::queue + std::mutex 做跨线程消息队列
把协议解析、DB 查询、技能计算全塞进 epoll 线程?那单核吞吐上不去,延迟还抖动。游戏后端真正的扩展性,不在连接数,而在单位时间能跑多少帧逻辑。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
典型做法是:1 个网络线程(负责收发 + 解包) + N 个逻辑线程(处理玩家行为、世界同步)。两者靠无锁队列或带锁队列通信。别迷信无锁 —— std::queue 配 std::mutex 在千级 QPS 下完全够用,且代码清晰、调试友好。
- 网络线程收到完整包后,构造
std::shared_ptr<msgbase></msgbase>,push 到全局std::queue,再 notify 逻辑线程条件变量 - 逻辑线程用
wait_for()避免忙等,超时可做 tick 逻辑(如移动插值、AOI 更新) - 别把 protobuf 序列化/反序列化放网络线程做 —— 字节流解析耗 CPU,会拖慢 IO 吞吐
std::unordered_map 存玩家 ID 映射,但注意 rehash 时的迭代器失效风险
玩家上线后要快速通过 player_id 查到对应连接或角色对象,std::unordered_map<int64_t std::shared_ptr>></int64_t> 是最直觉的选择。但它有个隐蔽坑:插入导致扩容 rehash 时,所有现存迭代器、指针、引用全部失效。
如果你在遍历 map 的同时又在另一线程 insert,或者在 for-range 循环里 erase 其他 key,程序大概率 crash 或静默错乱。
- 遍历时改写用
std::shared_mutex(C++17)读锁 + 写锁分离,比纯std::mutex更细粒度 - 避免在遍历中调用
erase()—— 改用erase_if()(C++20)或先收集待删 key 再批量删 - 如果 ID 是连续整数且范围可控(如 1~100w),考虑用
std::vector<:shared_ptr>></:shared_ptr>+ 空槽位标记,性能更好也更安全
扩展性不是堆机器或加线程数,而是每个模块是否能在不改结构的前提下横向切分。比如把 AOI、战斗、聊天拆成独立服务,靠 IPC 或轻量 RPC 通信 —— 那时候 std::unordered_map 就只负责本进程内的瞬时映射,不再承担全局状态职责。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










