从零实现一个简易的游戏服务器:连接管理
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 返回的可能是半个包,也可能是三个半包。所以:
- 读到的字节一律追加进
readBuffer - 循环尝试解包,直到长度不足
- 解出的包从缓冲区前部移除
不要假设「读一次就是一个完整包」,这是新手最常见的翻车点。
边界情况
accept返回EMFILE:进程 fd 用满了,此时不要关监听套接字- 对端半关闭:
recv返回 0 表示对方不再发数据,但仍可能收数据 - 心跳超时:用
lastActiveAt而不是定时器数量
把这几条处理干净,连接层就算立住了。