游戏卡顿白皮书 › L8 Socket 与协议
Nagle 算法 + 延迟 ACK Nagle + delayed ACK (TCP_NODELAY off)
原因 ID sk-nagle · 主责 研发团队·服务器开发 · 配合 研发团队·客户端开发
在含图示和实验的完整版中打开此卡片 →
把小包攒起来再发的 Nagle 算法,和推迟发送 ACK 的延迟 ACK 叠加在一起,消息每拆开写一次,就会延迟 40~200 ms。
起因 没开 TCP_NODELAY,却把小消息拆成多次写入 → 结果 发送方在等 ACK,接收方却推迟发送 ACK → 画面表现 线路 ping 很低,所有操作却都固定地慢半拍,表现为操作延迟
- 症状
- 操作延迟
- 因素
- 延迟
- 谁会遇到
- 全服, 只有我
- 何时出现
- 一直, 做特定操作时
- 负责方
- 主责 研发团队·服务器开发 · 配合 研发团队·客户端开发
- 研发团队要做的事
- 服务器:开启 TCP_NODELAY;把一个 tick 内的消息攒起来一次写入;关闭接收方延迟 ACK 的办法(Linux 的 TCP_QUICKACK 只能短暂维持,Windows 要逐台 PC 改注册表)游戏无法可靠掌控,不要依赖。客户端:开启 TCP_NODELAY;把一帧内的消息攒起来一次写入。
- 数值参考
- 延迟 ACK 在 Linux 上通常为 40 ms(视情况最长 200 ms)。Windows 旧版本为 200 ms,新版本为 40 ms(Windows Server 2019 默认模板为 40 ms)。延迟 ACK 由接收方的操作系统决定,所以服务器开着 Nagle 拆分发送消息时,视接收端 PC 不同,每次可能延迟 40~200 ms。
- 监控图上
- 一直偏高 · 操作响应时间(游戏内 RTT)
- 查看位置
- 在服务器侧抓包(tcpdump、Wireshark)中查看请求与响应之间的间隔,确认服务器、客户端代码是否开启了 TCP_NODELAY
- 确认依据
- 线路 ping 很低,小包之间却反复出现 40 ms(Windows 旧版本为 200 ms)左右的空档,且空档在对方的 ACK 到达后立即结束。开启 TCP_NODELAY 后消失
- 排除依据
- 响应间隔与线路 ping 相近则不是这个原因。游戏服务器生成响应慢,看服务器处理(“消息队列积压”)
- 确认手段
- 运维工具即可确认(无需游戏代码)
出处
- RFC 9293: Transmission Control Protocol (TCP) IETF
Nagle 在有未确认数据时把小数据攒起来;必须能按连接关闭;延迟 ACK 小于 0.5 秒;两者叠加的问题 - include/net/tcp.h (Linux v6.12) Linux kernel
Linux 延迟 ACK 最小为 TCP_DELACK_MIN(HZ/25 = 40 ms),最大为 TCP_DELACK_MAX(HZ/5 = 200 ms) - TCP improvements in the Windows network stack (IETF 98 TCPM) Microsoft
Windows 把默认延迟 ACK 超时改为 40 ms(2017 年发布) - TCP Templates for Windows Server 2019 – How to tune your Windows Server Transports (Advanced users only 😉) Microsoft
Server 2019 模板:DelayedAckTimeout 40 ms,MaxSynRetransmissions 2,InitialRto 3000 ms - Design issues - Sending small data segments over TCP with Winsock Microsoft
旧版 Windows TCP 收到数据后会启动 200 ms 的延迟 ACK 定时器,而 Nagle 默认开启,小包要等 ACK;用 TCP_NODELAY 解决
相关原因
同一层:L8 Socket 与协议
其他层中同样导致“操作延迟”的原因
查看含图示和实验的原卡片