한국어English日本語简体中文繁體中文DeutschไทยTiếng ViệtРусскийPortuguês (Brasil)EspañolBahasa Indonesia

游戏卡顿白皮书 › TCP 重传的根本原因

零窗口(看起来像重传的停顿) Zero window, often mistaken for retransmission

原因 ID rt-zero-window · 主责 研发团队·客户端开发 · 配合 研发团队·服务器开发, 运维团队·系统运维

在含图示和实验的完整版中打开此卡片 →

接收方程序没有及时读取 socket,缓冲区满了之后,发送方就会停止发送,只发零窗口探测。这与线路无关。

起因 客户端帧停住或服务器线程阻塞,读不了 socket → 结果 接收窗口变为 0,发送方停止发送,只发探测(间隔越来越长) → 画面表现 卡住后快进。抓包中能看到“ZeroWindow”,没有丢包

症状
卡住, 快进
因素
停顿
谁会遇到
只有我, 全服
何时出现
人多的时候, 偶尔随机
负责方
主责 研发团队·客户端开发 · 配合 研发团队·服务器开发, 运维团队·系统运维
研发团队要做的事
先在抓包中确认是哪一方发出 ZeroWindow(读不了 socket 的一方);网络接收放在独立线程中持续读取;接收缓冲区设为合适大小。客户端:解决加载、GC 等导致帧停住的原因。服务器:解决读 socket 的线程被阻塞的原因。
运维团队要做的事
把服务器 nstat 的 TcpExtTCPToZeroWindowAdv(服务器通告接收窗口为 0 的次数)加入监控(增加说明问题在服务器侧,转交服务器开发);提供服务器侧抓包。
监控图上
断流后集中到达 · 每个连接的接收量,零窗口次数
查看位置
在抓包中用 Wireshark 过滤条件 tcp.analysis.zero_window 找出通告窗口为 0 的一方。服务器 nstat 中分别看 TcpExtTCPToZeroWindowAdv(服务器通告窗口为 0)和 TcpExtTCPWinProbe(对方窗口为 0 时发送探测),并看服务器 socket 的 Recv-Q(ss 中程序尚未读取的字节)
确认依据
停顿期间没有重传,只有零窗口和探测在往来。服务器的 TcpExtTCPToZeroWindowAdv 或服务器 socket 的 Recv-Q 增加,说明服务器没能及时读取;TcpExtTCPWinProbe 增加,说明客户端没能及时读取
排除依据
抓包中没有零窗口,且同样的数据被重新发送,看丢包或虚假重传方面的原因
确认手段
运维工具即可确认(无需游戏代码)
真实案例
Roblox 2021: Roblox 73 小时故障:服务发现(Consul)集群的争用问题

出处

  1. RFC 9293: Transmission Control Protocol (TCP) IETF
    接收窗口为 0 时,发送方发送零窗口探测,探测间隔按指数增长
  2. 7.5. TCP Analysis Wireshark
    TCP ZeroWindow:接收方通告窗口为 0、让发送方停止发送的数据包
  3. Display Filter Reference: Transmission Control Protocol Wireshark
    tcp.analysis.zero_window、tcp.analysis.zero_window_probe 显示过滤器
  4. SNMP counter Linux kernel
    TcpExtTCPToZeroWindowAdv:接收窗口从非 0 值通告为 0 的次数
  5. net/ipv4/proc.c Linux kernel
    nstat 中的计数器名 TCPToZeroWindowAdv、TCPWinProbe
  6. net/ipv4/tcp_output.c Linux kernel
    TCPWinProbe:对方接收窗口为 0 时,每发送一次探测(tcp_send_probe0)就加一
  7. net/ipv4/tcp_diag.c Linux kernel
    对已建立的连接而言,ss 的 Recv-Q 是已收到但程序尚未读取的字节数

相关原因

同一层:TCP 重传的根本原因

其他层中同样导致“卡住”的原因

查看含图示和实验的原卡片