遊戲 Lag 白皮書 › L8 Socket 與協定
Nagle 演算法 + 延遲 ACK Nagle + delayed ACK (TCP_NODELAY off)
原因 ID sk-nagle · 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發)
在含圖解與實驗的完整版中開啟卡片 →
把小封包集中起來送的 Nagle 演算法,與延後送出 ACK 的延遲 ACK 互相牽制,每次把訊息分段寫入就會延遲 40~200ms。
為什麼 沒有開啟 TCP_NODELAY 就分段寫入小訊息 → 於是 傳送端在等 ACK,接收端卻延後送出 ACK → 畫面上 線路 ping 很低,每個動作卻都固定慢半拍,出現輸入延遲
- 症狀
- 輸入延遲
- 因素
- 延遲
- 誰會遇到
- 整個伺服器, 只有我
- 何時
- 一直都有, 做特定動作時
- 負責單位
- 主要負責 遊戲開發團隊(伺服器開發) · 協同 遊戲開發團隊(用戶端開發)
- 遊戲開發團隊要做的事
- 伺服器:開啟 TCP_NODELAY;把一個 tick 內的訊息集中起來一次寫入;關閉接收端延遲 ACK 的方法(Linux 的 TCP_QUICKACK 只維持一小段時間,Windows 要逐台電腦修改登錄檔)遊戲無法確實掌控,不要依賴。用戶端:開啟 TCP_NODELAY,把一個畫格內的訊息集中起來一次寫入。
- 數值參考
- Linux 的延遲 ACK 通常是 40ms(視情況最多 200ms)。Windows 舊版是 200ms,現在的版本是 40ms(Windows Server 2019 預設範本為 40ms)。延遲 ACK 由接收端 OS 決定,所以伺服器在 Nagle 開啟的狀態下分段送出訊息時,依接收端電腦不同,每次可能延遲 40~200ms。
- 圖表上
- 一開始就一直偏高 · 動作回應時間(遊戲內 RTT)
- 查看位置
- 在伺服器端的封包擷取(tcpdump、Wireshark)中看請求與回應之間的間隔,並確認伺服器與用戶端程式碼是否開啟 TCP_NODELAY
- 符合的跡象
- 線路 ping 很低,小封包之間卻反覆出現 40ms(Windows 舊版為 200ms)左右的空白,且空白在對方的 ACK 到達後隨即結束。開啟 TCP_NODELAY 後消失
- 不符合的跡象
- 回應間隔與線路 ping 相近就不是這個原因。遊戲伺服器產生回應太慢時,問題在伺服器處理(「訊息佇列積壓」)
- 確認方式
- 用基礎設施工具確認(不需要遊戲程式碼)
出處
- RFC 9293: Transmission Control Protocol (TCP) IETF
有尚未收到 ACK 的資料時,Nagle 會把小資料先累積起來;必須能逐條連線關閉;延遲 ACK 需小於 0.5 秒;兩者互相牽制的問題 - include/net/tcp.h (Linux v6.12) Linux kernel
Linux 延遲 ACK 最小為 TCP_DELACK_MIN(HZ/25 = 40ms),最大為 TCP_DELACK_MAX(HZ/5 = 200ms) - TCP improvements in the Windows network stack (IETF 98 TCPM) Microsoft
Windows 預設的延遲 ACK 逾時改為 40ms(2017 年發表) - TCP Templates for Windows Server 2019 – How to tune your Windows Server Transports (Advanced users only 😉) Microsoft
Server 2019 範本:DelayedAckTimeout 40ms、MaxSynRetransmissions 2、InitialRto 3000ms - Design issues - Sending small data segments over TCP with Winsock Microsoft
舊版 Windows TCP 收到資料時會啟動 200ms 的延遲 ACK 計時器,而 Nagle 預設開啟,小封包因此要等 ACK;以 TCP_NODELAY 解決
相關原因
同一層:L8 Socket 與協定
同一症狀(輸入延遲)在其他層的原因
查看含圖解與實驗的完整版卡片