遊戲 Lag 白皮書 › TCP 重傳的根本原因
連線請求(SYN)重傳 SYN retransmission on connect
原因 ID rt-syn · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施), 基礎設施團隊(網路基礎設施), 遊戲開發團隊(用戶端開發)
在含圖解與實驗的完整版中開啟卡片 →
連線請求因連線等待佇列(backlog)溢位或被防火牆阻擋而消失時,用戶端 OS 會從 1 秒後開始,逐步拉長間隔重送。
為什麼 維護剛結束時連線暴增,伺服器的連線等待佇列溢位,或是防火牆、DDoS 防護丟棄 SYN → 於是 用戶端 OS 從 1 秒後開始以固定間隔重傳 SYN(舊版 Linux 為 1 秒 → 2 秒 → 4 秒) → 畫面上 按下連線按鈕後,延遲剛好是 1 秒、3 秒這種整秒數,持續失敗就連不上/無限讀取
- 症狀
- 連不上/無限讀取
- 因素
- 遺失
- 誰會遇到
- 整個伺服器, 特定地區/電信業者
- 何時
- 剛登入/維護剛結束
- 負責單位
- 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施), 基礎設施團隊(網路基礎設施), 遊戲開發團隊(用戶端開發)
- 遊戲開發團隊要做的事
- 伺服器:加大 listen 的 backlog 參數(與 somaxconn 一起調)、讓遊戲伺服器及時呼叫 accept、登入排隊系統。用戶端:拉長連線重試間隔(隨機分散)。
- 基礎設施團隊要做的事
- 伺服器設備/OS:伺服器連線等待佇列溢位可用 nstat 的 TcpExtListenOverflows、TcpExtListenDrops 與 log 中的「Possible SYN flooding」警告確認、加大 somaxconn(與 listen 參數一起調)、使用 SYN cookie。網路:放寬防火牆與 DDoS 防護的 SYN 限制。
- 數值參考
- Linux(包括 Android)第一次 SYN 重傳在 1 秒後。舊版 kernel 之後間隔每次加倍,在第 1、3、7、15 秒……時重送,6.5 以上則在 1、2、3、4、5 秒重送五次後才開始加倍(7、11、19 秒……)(tcp_syn_linear_timeouts=4)。Android 手機即使更新 OS,很多仍沿用出廠時的 kernel,所以即使是同一個 Android 版本,不同裝置也可能不一樣。不論哪一種,全部失敗後約 2 分鐘就會放棄。Windows 依版本與設定從 1 秒或 3 秒開始拉長,重送次數為 2~4 次,因此 20~30 秒就會放棄(該電腦的值可用 netsh int tcp show global 的 Max SYN Retransmissions 確認)。
- 圖表上
- 剛開服或維護結束後暴增 · 連線嘗試次數、連線等待佇列溢位次數
- 查看位置
- 在伺服器的 nstat 查看 TcpExtListenOverflows、TcpExtListenDrops,以及 dmesg 的「Possible SYN flooding on port」警告,並用 ss -lnt 查看監聽 socket 的 Recv-Q(等待 accept 的連線數)是否碰到 Send-Q(backlog 上限)。用伺服器端擷取確認 SYN 是否抵達、是否回傳 SYN-ACK
- 符合的跡象
- 維護剛結束連線暴增時,TcpExtListenOverflows 增加,Recv-Q 貼著 Send-Q。擷取中同一個用戶端的 SYN 以整秒間隔重複送來,伺服器卻沒有回應
- 不符合的跡象
- SYN 沒有到達伺服器、伺服器計數器也沒有變化時,是前端的防火牆或 DDoS 防護丟棄了 SYN,要查該設備的 SYN 限制與丟棄 log。伺服器已送出 SYN-ACK 但連線仍慢時,是回程方向的遺失
- 確認方式
- 用基礎設施工具確認(不需要遊戲程式碼)
出處
- include/net/tcp.h Linux kernel
第一次 RTO TCP_TIMEOUT_INIT = 1 秒(RFC 6298 的初始值) - RFC 6298: Computing TCP's Retransmission Timer IETF
初始 RTO 1 秒,每次重傳加倍 - IP Sysctl Linux kernel
tcp_syn_retries 預設 6、tcp_syn_linear_timeouts 預設 4(SYN RTO 1、1、1、1、1、2、4……),最後一次重傳在 67 秒、131 秒時放棄,somaxconn 預設 4096,tcp_syncookies 預設 1 - tcp: make the first N SYN RTO backoffs linear Linux kernel
把 SYN 重傳的前幾次改為固定間隔的 commit,自 Linux 6.5 起(預設值 4 沿用 macOS、iOS 的做法) - Android common kernels Android (Google)
5.10~6.18 的通用 kernel(common kernel)同時受到支援,舊平台用的 kernel(例:android14-6.1)也可用於新 Android 裝置的出廠或升級 - TcpMaxConnectRetransmissions Microsoft
舊版 Windows 預設值:SYN 重傳 2 次,第一次等待 3 秒,之後每次加倍,最後一次之後再等加倍的時間才放棄(3+6+12=21 秒) - TCP/IP connectivity issues troubleshooting Microsoft
SYN 重傳次數依 OS 而異,可用 netsh int tcp show global 的 Max SYN Retransmissions 確認 - listen(2) — Linux manual page Linux man-pages
listen 的 backlog 參數會被截斷為 somaxconn(自 Linux 5.4 起預設 4096,之前為 128) - SNMP counter Linux kernel
accept 佇列滿時會丟棄 SYN,TcpExtListenOverflows 與 TcpExtListenDrops 一起增加;TcpExtTCPSynRetrans - net/ipv4/tcp_input.c Linux kernel
「Possible SYN flooding on port …」log 訊息 - net/ipv4/tcp_diag.c Linux kernel
監聽 socket 在 ss 中的 Recv-Q 為等待 accept 的連線數,Send-Q 為 backlog 上限
相關原因
同一層:TCP 重傳的根本原因
同一症狀(連不上/無限讀取)在其他層的原因
查看含圖解與實驗的完整版卡片