《游戏服务端编程实践》3.1.1 TCP / UDP / WebSocket 对比
一、引言:游戏通信的本质是“实时性 + 一致性”的平衡
在游戏开发中,通信协议决定了“游戏世界如何同步”。 它不仅仅是“传输数据”,而是塑造了整个游戏架构的形态。
不同类型的游戏对“实时性、可靠性、带宽、顺序性”的要求差异极大:
| 游戏类型 | 实时性 | 容忍丢包 | 数据顺序要求 | 典型协议 |
|---|---|---|---|---|
| SLG / RPG | 低(秒级) | 不容忍 | 必须按序 | TCP / WebSocket |
| MOBA / FPS | 高(毫秒级) | 可容忍 | 局部无序可接受 | UDP / QUIC |
| 休闲社交类 | 中 | 不容忍 | 强一致 | WebSocket |
| 直播 / 弹幕 / 聊天 | 中高 | 可容忍 | 不重要 | UDP / WebSocket |
因此,理解 TCP、UDP 与 WebSocket 的机制与差异,是构建高效游戏网络层的前提。
二、TCP:面向连接的可靠传输协议
2.1 核心特征
TCP(Transmission Control Protocol)是一个面向连接、可靠的字节流协议, 由操作系统的内核 TCP 栈负责维护。
| 特性 | 描述 |
|---|---|
| 连接导向(Connection-oriented) | 三次握手建立连接,四次挥手关闭 |
| 可靠传输(Reliable Delivery) | 通过确认(ACK)与重传机制确保数据到达 |
| 有序性(In-order Delivery) | 数据按序排列,不会乱序 |
| 流量控制(Flow Control) | 避免发送方淹没接收方 |
| 拥塞控制(Congestion Control) | 根据网络情况动态调整速率 |
TCP 就像“顺序送快递”:即使中途延误,也保证包裹按顺序抵达。
2.2 TCP 的连接建立(3-way handshake)
sequenceDiagram
Client->>Server: SYN (同步请求)
Server-->>Client: SYN + ACK (确认连接)
Client->>Server: ACK (确认)
Note right of Server: 连接建立
2.3 可靠传输机制
TCP 使用序列号 + 确认号机制:
- 每个包带序列号(seq);
- 接收方返回 ACK;
- 丢失包将触发重传;
- 超时自动调整窗口大小。
TCP 在可靠性和顺序性上非常强,但代价是延迟增加。
2.4 在游戏中的适用场景
| 适合场景 | 示例 |
|---|---|
| RPG / SLG / MMO | 登录、战斗结果、任务状态 |
| 策略与模拟类 | 行为决策、回合确认 |
| 商城 / 聊天 / 经济系统 | 防丢消息、强一致性需求 |
2.5 TCP 的局限
| 问题 | 描述 | 对游戏的影响 |
|---|---|---|
| 延迟积累(Head-of-line blocking) | 丢一个包会阻塞后续所有包 | 玩家感觉卡顿 |
| 系统调用频繁 | 每次收发都需进入内核 | CPU 开销大 |
| 粘包 / 拆包问题 | 连续发送数据会被合并 | 需应用层封包 |
| 重传延迟 | 网络抖动会造成重传等待 | 影响实时性 |
2.6 TCP 粘包 / 拆包处理示例(Java)
ByteBuffer buffer = ByteBuffer.allocate(1024);
int length = inputStream.read(buffer.array());
if (length > 0) {
while (buffer.remaining() >= HEADER_SIZE) {
int msgLen = buffer.getInt();
if (buffer.remaining() < msgLen) break;
byte[] msg = new byte[msgLen];
buffer.get(msg);
handleMessage(msg);
}
}
三、UDP:无连接的高性能传输协议
3.1 核心特征
UDP(User Datagram Protocol)是一个无连接、不可靠、不保证顺序的协议。 它直接将数据包(Datagram)发到目标地址,不建立连接,不确认,不重传。
| 特性 | 描述 |
|---|---|
| 无连接(Connectionless) | 不建立握手,直接发包 |
| 不可靠(Unreliable) | 丢包不重传 |
| 无顺序保证(Out-of-order possible) | 包可能乱序到达 |
| 低延迟(Low Latency) | 减少握手与确认 |
| 轻量级 | 头部仅 8 字节 |
UDP 就像“广播喊话”:说出去就算,听到与否由对方决定。
3.2 在游戏中的应用场景
| 类型 | 示例 |
|---|---|
| FPS / 射击类 | 玩家移动、射击方向、帧同步 |
| MOBA | 技能释放、坐标同步 |
| 实时对战 | 房间广播、Tick 推进 |
| 实时媒体 | 语音、弹幕、直播流 |
对这些游戏来说,“宁可丢包,也不能卡顿” 是首要原则。
3.3 UDP 优势与劣势
| 优势 | 劣势 |
|---|---|
| ✅ 延迟低 | ❌ 不可靠 |
| ✅ 开销小 | ❌ 乱序可能 |
| ✅ 不阻塞 | ❌ 无拥塞控制 |
| ✅ 适合实时 | ❌ 自行处理重传与校验 |
UDP 不会帮你“兜底”,你得自己实现可靠层。
3.4 游戏常用可靠 UDP 封装(RUDP)
许多游戏框架会在 UDP 之上自建“可靠层”,例如:
- ACK 序列确认;
- 丢包重发;
- 滑动窗口;
- 包序号与校验。
Go 示例:
type Packet struct {
Seq uint32
Ack uint32
Data []byte
}
func SendReliable(conn *net.UDPConn, pkt Packet) {
buf := Encode(pkt)
conn.Write(buf)
WaitAck(pkt.Seq)
}
RUDP(Reliable UDP) 是许多实时游戏的底层基础,例如 Quake、Overwatch、CSGO 都使用自研 UDP 可靠协议。
四、WebSocket:基于 TCP 的全双工通信协议
4.1 核心特征
WebSocket 是建立在 TCP 之上的“全双工通信协议”, 它使浏览器或客户端能够与服务器持续保持连接,支持双向实时数据传输。
| 特性 | 描述 |
|---|---|
| 基于 TCP | 使用同样的可靠传输机制 |
| 全双工(Full-duplex) | 客户端和服务端可同时发送 |
| 长连接 | 一次握手,多次通信 |
| 跨平台 / 跨语言 | 浏览器、Unity、手机端皆可使用 |
| 适合低频实时通信 | 聊天、匹配、通知等 |
WebSocket 是 Web 时代的“游戏 TCP 长连接方案”。
4.2 握手过程(Upgrade from HTTP)
sequenceDiagram
Client->>Server: HTTP GET /chat (Upgrade: websocket)
Server-->>Client: 101 Switching Protocols
Note right of Server: 通信协议升级完成
Client->>Server: send("hello")
Server-->>Client: send("world")
它通过 HTTP 升级握手,建立后长期保持 TCP 通道。
4.3 在游戏中的典型应用
| 模块 | 功能 | 特点 |
|---|---|---|
| 登录 / 匹配 / 聊天 | 实时通信、通知 | 数据小,交互频繁 |
| 休闲类 H5 游戏 | 状态同步 | 浏览器原生支持 |
| 轻量回合制游戏 | 指令传递 | 对延迟容忍 |
| 跨端心跳 / 热更通道 | 通用数据推送 | 管理与监控层常用 |
4.4 WebSocket 的实现成本与限制
| 优点 | 缺点 |
|---|---|
| ✅ 开发友好(内建于浏览器) | ❌ 基于 TCP,延迟相对高 |
| ✅ 双向通信 | ❌ 需要心跳维持 |
| ✅ 安全(TLS 支持) | ❌ 性能弱于原生 TCP |
| ✅ 与 HTTP 共存 | ❌ 不适合高频帧同步 |
因此,WebSocket 适合逻辑层通信,而不是实时战斗层。
五、三种协议的系统对比
| 特征 | TCP | UDP | WebSocket |
|---|---|---|---|
| 连接类型 | 面向连接 | 无连接 | 长连接(基于TCP) |
| 可靠性 | ✅ 高 | ❌ 无 | ✅ 高 |
| 顺序性 | ✅ 保证 | ❌ 不保证 | ✅ 保证 |
| 延迟 | 中 | 低 | 中高 |
| 丢包处理 | 自动重传 | 需自实现 | 自动重传 |
| 带宽占用 | 高 | 低 | 高 |
| 适用场景 | MMO、SLG | FPS、MOBA | H5、聊天、社交 |
| 应用层封包 | 需要 | 自定义 | 框架内封装 |
| 浏览器支持 | 否 | 否 | ✅ 是 |
| 常见库 | Netty, Mina | ENet, KCP | Socket.IO, ws |
| 性能调优 | TCP_NODELAY、Nagle | Buffer/RTT | 心跳间隔、压缩 |
六、实际项目中的混合架构模式
在现代游戏项目中,通常不会单独使用一种协议,而是组合多种通信层:
graph TD
A["客户端"] --> B["TCP (登录/数据)"]
A --> C["UDP (战斗/同步)"]
A --> D["WebSocket (聊天/通知)"]
| 协议 | 负责内容 |
|---|---|
| TCP | 登录、验证、经济系统 |
| UDP | 战斗逻辑、帧同步 |
| WebSocket | 聊天、匹配、监控 |
例如:
- 登录 → TCP;
- 战斗 → UDP;
- 断线重连 / 消息通知 → WebSocket;
- GM 管理 / 分析 → HTTP + WebSocket。
七、Java 实践:混合协议网关架构
public class GameGateway {
private final TcpServer tcp;
private final UdpServer udp;
private final WebSocketServer ws;
public GameGateway() {
tcp = new TcpServer(8080);
udp = new UdpServer(8081);
ws = new WebSocketServer(8082);
}
public void start() {
tcp.start();
udp.start();
ws.start();
}
}
- 逻辑服可以通过 统一消息总线(Message Bus) 路由不同协议的输入;
- 各模块独立维护连接;
- 支持多协议并行处理。
八、Go 实践:统一协议抽象接口
type Conn interface {
Send(data []byte) error
Recv() ([]byte, error)
Close() error
}
func HandleConnection(c Conn) {
for {
msg, err := c.Recv()
if err != nil {
break
}
process(msg)
}
}
通过统一接口封装 TCP / UDP / WebSocket, 上层逻辑可透明使用同一套通信抽象。
九、协议选型建议
| 游戏类型 | 推荐协议 | 理由 |
|---|---|---|
| MMORPG / SLG | TCP / WebSocket | 强一致、顺序敏感 |
| FPS / 动作类 | UDP + 自定义可靠层 | 低延迟关键 |
| MOBA / 实时竞技 | UDP + 帧同步 | 稳定流畅 |
| 休闲 / 卡牌 | WebSocket / TCP | 逻辑简单、易部署 |
| H5 / 小程序游戏 | WebSocket | 浏览器友好、无插件 |
| 跨端系统模块 | HTTP + WS | 管理、匹配、聊天 |
十、总结与设计启示
网络协议不是“选择一种”,而是“根据游戏特性设计一套组合策略”。
- TCP = 稳定性 → 适合登录、任务、经济等不容错逻辑。
- UDP = 实时性 → 适合战斗同步、动作游戏。
- WebSocket = 兼容性 → 适合 H5、跨平台、低频通信。
- 混合方案 = 工业标准 → 多协议并行,统一路由与序列化层。
在高并发分布式架构下,真正的“协议选型能力”是一种系统设计思维: 你选择的不是传输方式,而是体验与公平的平衡点。