从零实现一个简易的游戏服务器:连接管理

C++游戏服务器

连接管理的基本形状

服务器要做的第一件事,是把「谁连着」记清楚。最小可用的结构只需要一张连接表和一个事件循环。

连接表

struct Connection {
  int fd;
  std::string readBuffer;
  std::string writeBuffer;
  uint64_t lastActiveAt;
};

std::unordered_map<int, Connection> conns;

用 fd 作为键的好处是回收直接删表项,坏处是 fd 会被复用——所以删除必须在关闭之后立刻做,不能延后到下一次循环。

事件循环

阶段 做什么 常见错误
poll 等待可读可写 超时设 0,导致空转烧 CPU
read 读进缓冲区 假设一次读完,忽略半包
process 解包、分发 在缓冲区里反复 memmove
write 冲刷发送队列 忽略 EAGAIN 直接当断开

半包与粘包

TCP 是字节流,一次 recv 返回的可能是半个包,也可能是三个半包。所以:

  1. 读到的字节一律追加进 readBuffer
  2. 循环尝试解包,直到长度不足
  3. 解出的包从缓冲区前部移除

不要假设「读一次就是一个完整包」,这是新手最常见的翻车点。

边界情况

  • accept 返回 EMFILE:进程 fd 用满了,此时不要关监听套接字
  • 对端半关闭:recv 返回 0 表示对方不再发数据,但仍可能收数据
  • 心跳超时:用 lastActiveAt 而不是定时器数量

把这几条处理干净,连接层就算立住了。

从零实现一个简易的游戏服务器:连接管理 | Kasure