TCP 重送讓工業相機通訊卡幾秒時 - RFC1323 timestamp 與重送等待的切分
· 小村 豪 · TCP, 網路, 故障調查, Windows 開發, 工業相機
在工業相機或設備控制的通訊中,最棘手的是 平均很快,但偶爾卡幾秒。再現率很低,平時什麼事也沒發生,因此 UI、執行緒、GC、相機 SDK、NIC、交換器,每一個都看起來有一點嫌疑。
本次要處理的,是工業相機控制應用與主機之間的 TCP 通訊,偶爾會卡個幾秒。一查下去,真正的兇手並不是應用停住,而是 封包遺失造成的 TCP 重送等待。此外,只要啟用 RFC1323 系的 timestamp 機制(現在的整理是 RFC 7323),就能把本系統的等待時間壓到最低。
設備名稱、組態與數字有做一般化,但整體的思路可以直接用在實務上。
1. 先下結論(一句話)
- 偶爾卡幾秒的 TCP 通訊,兇手其實可能不是應用停住,而是 封包遺失後的重送等待
- 如果封包擷取看到
Retransmission與明顯的時間差,且停頓時間與 RTO 的等待方式吻合,就相當可疑 - TCP timestamps option 是為了 RTT 測量與 PAWS,同時能消除重送時 RTT 測量的模糊
- 本案例透過啟用 RFC1323 系的 timestamp,讓 RTO 估計停留在過時且保守狀態的時間縮短,秒級停頓降到最低
- 不過,這 不是消滅封包遺失本身的魔法。物理層、NIC、交換器、中間設備、驅動、緩衝設計的重新檢視都仍然需要另外做
簡言之,「偶爾卡幾秒」若真正的原因是 TCP 內的等待時間,那只加強應用的 retry 其實打不到要害。先看 wire,先確定是不是重送等待會更快。
2. 症狀的樣貌
2.1. 應用仍在跑,只有回應卡住幾秒
最讓人困惑的是,應用整體看起來並沒有凍住。
- UI 沒有完全死掉
- 行程也沒掛
- CPU 也沒有貼在 100%
- 但相機控制命令的回應,偶爾 會掉個幾秒
這種症狀,很難與應用內的 deadlock 或無窮迴圈區分。而且在設備控制上,1 次的數秒停頓就會變成整條線停線的印象。平均值再漂亮,現場體感也會很差。
2.2. 因為低頻,光看日誌不太看得出來
這類故障麻煩的地方在於發生頻率很低,可能一小時一次、半天一次、或僅在條件重疊時才發生。
只看日誌會出現:
- 應用日誌上顯示「送出了」「沒回來」
- 接收端日誌則是「什麼都沒收到」
- 同一時段剛好也發生其他事件,兇手分散
這時若只用應用日誌去還原因果,很容易陷進泥沼。更快的做法是往下降一層看通訊層。
3. 實際發生了什麼(示意圖)
3.1. 從封包遺失進入重送等待
這次的劇情很單純。途中某處封包掉了,送出端等不到 ACK,於是等 RTO 到期才重送。
sequenceDiagram
participant Host as 主機應用
participant Net as 網路
participant Cam as 相機端
Host->>Net: 控制命令 (Seq=N)
Note over Net: 這裡遺失
Note over Host: 沒 ACK 只好等
Note over Host: 這次請求的恢復需要等 RTO
Host->>Net: 重送控制命令
Net->>Cam: 重送封包到達
Cam-->>Net: ACK
Net-->>Host: ACK
Note over Host: 此時通訊恢復
從應用視角看像是「卡了幾秒」,從 TCP 視角其實只是「ACK 還沒來,所以在等重送計時器到期」。雖然很樸素,但這種停頓方式相當常見。
本次的控制通訊以小型的 request/response 為主,一次互動不會送出大量未 ACK 的資料。因此在還沒收到足夠的 duplicate ACK 以啟動 fast retransmit 之前,RTO 等待 就會先浮出來。
3.2. 秒級停頓與 RTO 的形狀吻合
TCP 的重送等待雖有實作差異,大體上採取保守的等待策略。RFC 6298 中,初始 RTO 以 1 秒為基準,計算值若低於 1 秒則向上取到 1 秒;發生 timeout 時再加倍。
flowchart LR
A[封包遺失] --> B[沒有 ACK]
B --> C[等 RTO]
C --> D[重送]
D --> E{有 ACK 嗎?}
E -- 是 --> F[通訊恢復]
E -- 否 --> G[RTO 加倍]
G --> C
因此,希望數百毫秒內結束的情境,條件一差就可能看到 1 秒、2 秒、4 秒這樣的等待。本次「偶爾卡幾秒」的形狀,和這個模式貼合得很自然。
4. 調查時的觀察重點
4.1. 先排除應用內的停頓原因
沒有直接把 TCP 當兇手,而是先把應用側的典型原因排除。
| 確認的項目 | 查看的理由 | 本次的結論 |
|---|---|---|
| UI 執行緒 / Worker 執行緒 | 檢查 hang 或互等 | 不是主因 |
| CPU 使用率 | 檢查高負載引起的延遲 | 停頓時沒有貼滿 |
| GC / 記憶體壓力 | 檢查暫停 | 停頓時間形狀不符 |
| 相機 SDK 呼叫 | 檢查 SDK 內部等待 | 與 wire 上的延遲不一致 |
| 封包擷取 | 檢查通訊層的重送 | 從這裡看到原因線索 |
這邊的重點是不要只靠應用日誌的時刻判案。在設備控制應用上,上層的等待常只是下層等待的投影。
4.2. 用封包擷取確認重送
抓包後可以看到,停頓的時段有 TCP Retransmission,而且緊接在前面的是沒有回來的 ACK。
要看的項目例如:
- 是否出現同
Seq的重送 - 重送與前一包的時間差是否和停頓時間一致
- 是否不是
Dup ACK或Fast Retransmission,而是等 RTO 到期 - 問題的連線是否每次都在同一個
tcp.stream
當這幾點都吻合,「應用停住」的假設會大幅退位,「TCP 在等重送」的可能性就相當高。
4.3. 檢視協商到的 TCP option
接下來看的是連線建立時的 SYN / SYN-ACK。TCP 的 timestamp 是在 3-way handshake 階段協商,如果這裡沒有 TSopt,後續的段就不會用到。
sequenceDiagram
participant Host as 主機
participant Cam as 相機端
Host->>Cam: SYN + TSopt ?
Cam-->>Host: SYN/ACK + TSopt ?
Host->>Cam: ACK
Note over Host,Cam: 這裡協商成功,後續的段才有 TSopt 可用
若不看這裡就只改 OS 設定,常會出現「我明明開了,但怎麼沒效」這種低調事故。設定值沒有 wire 上的事實強。
5. RFC1323 timestamp 為何有效
在實務上還看得到「RFC1323 的 timestamp」這個叫法,但現行整理其實是 RFC 7323。本文沿用慣稱寫 RFC1323,意思就是 TCP timestamps option。
5.1. timestamp 是為了 RTTM 與 PAWS
TCP 的 timestamps option 主要用途有兩個:
- RTTM(Round-Trip Time Measurement)
- PAWS(Protect Against Wrapped Sequences)
這次發揮作用的是 RTTM。送出端的 TSval 會由對方在 ACK 裡以 TSecr 送回,送出端就能更細、更準地量測 RTT。
5.2. 消除重送情況下 RTT 測量的模糊
發生重送時,沒有 timestamp 就無法清楚判斷「這個 ACK 是在回最初送出,還是回重送」。這正是 Karn 演算法關注的問題。
RFC 6298 明確規定重送的段不能拿來取 RTT 樣本,因為不知道那是回哪個送出。但有 timestamps option 就能擺脫這種模糊:看 ACK 裡的 TSecr 就能判斷是哪一個 TSval 的段到了。
sequenceDiagram
participant Host as 送出端
participant Cam as 接收端
Host->>Cam: Seq=N, TSval=1000
Note over Host,Cam: 此段遺失
Note over Host: 沒 ACK 只好等
Host->>Cam: 重送 Seq=N, TSval=2000
Cam-->>Host: ACK, TSecr=2000
Note over Host: 可以辨認是在回哪一次送出
這正是本次改善的核心。
5.3. 為什麼本案能壓縮等待時間
本案中偶爾會發生封包遺失,每次都讓 RTT / RTO 的估計被保守地拉大。啟用 timestamp 後,連同重送的情境,RTT 的估計也能更新,避免 RTO 估計在過時狀態下一路膨脹。
換句話說,這次做的並不是讓 TCP 更快的魔法,而是 讓 TCP 不要過度謹慎觀望太久。
當然,RFC 7323 也不是宣稱「拿到更多 RTT 樣本就能一切解決」。它對 RTO 最佳化的影響確實有限。但 消除重送時的模糊 這點,對本案這種系統會直接派上用場。
要注意的地方:
- 這部分會受 TCP stack 實作影響
- timestamp 本身不會消除封包遺失
- 若物理層或中間設備有問題,根本原因在別處
- SACK、NIC 驅動、offload 設定、交換器端問題也都建議另外看
不過,像這次「遺失不是零,但真正痛的是秒級等待」的系統,它的效果往往相當直接。
6. 實際採取的對策
6.1. 啟用 timestamp
做法是讓連線兩端都能協商 timestamps option。Windows 系中有時用「RFC 1323 選項」來稱呼,會受到 OS 設定與網路設定的影響。
實務上比「設定畫面已啟用」更重要的是「實際 SYN / SYN-ACK 封包裡有 TSopt」。這點真的很重要。
6.2. 在 SYN / SYN-ACK 驗證 TSopt
啟用後接著確認下列 3 點:
- 問題連線的 SYN 是否有 TSopt
- SYN/ACK 是否也回了 TSopt
- 之後的資料段與 ACK 是否都持續帶著 TSopt
這幾項都確認後,才能說「這個連線真的有在使用 timestamps」。
6.3. 若啟用後仍沒改善要看哪裡
即使啟用 timestamp,下列情況改善仍會有限。
- 遺失率本身很高
- 中間設備會破壞、丟棄或改寫 TCP option
- NIC / 驅動 / offload 週邊另有問題
- 應用把全部流程掛在單一同步呼叫,一次等待看起來就像整體停頓
- 真正的主因其實不在 TCP,而是相機側處理停住或設備內佇列塞住
因此建議的對策順序是:
- 先在 wire 上確認是重送等待
- 看 TSopt 是否有協商
- 啟用 timestamps 後比較改善幅度
- 若仍有問題,再分別處理遺失源與應用設計
7. Wireshark 的觀察重點
切分時好用的顯示過濾器,例如:
tcp.stream eq <目標串流>
tcp.analysis.retransmission
tcp.analysis.fast_retransmission
tcp.analysis.lost_segment
tcp.options.timestamp.tsval
tcp.options.timestamp.tsecr
看的訣竅:
- 先用
tcp.stream把目標連線過濾出來 - 顯示
Time delta from previous displayed packet,可直接看停頓秒數 - 確認問題瞬間是否出現
Retransmission - 確認連線開頭的 SYN / SYN-ACK 是否協商了 TSopt
- 觀察 ACK 是否帶著
TSecr
對照日誌與封包時,要留意應用時間與擷取時間基準的偏差。基準一旦錯位,很容易把無辜的模組當成兇手。
8. 粗略的分流表
| 症狀 | 首先懷疑的 | 最先要做的 |
|---|---|---|
| 偶爾卡幾秒 | TCP 的 RTO 等待 | 以封包確認重送與時間差 |
| 幾乎在固定時機停住 | 應用內等待、設備側處理、硬性 timeout | 看執行緒、SDK 呼叫、設備日誌 |
| 高負載時才惡化 | CPU、GC、佇列堆積 | 看 CPU、中斷、記憶體、佇列長度 |
| 多個連線同時變差 | 物理層、交換器、中間設備 | 看 NIC、線材、埠統計、中介設備日誌 |
| 改了設定卻沒變化 | TCP option 沒真的協商上 | 重新確認 SYN / SYN-ACK |
最後一行真的常見。改設定的滿足感和 wire 上真的被採用,是兩回事。
9. 總結
本次的重點:
- 「偶爾卡幾秒」可能不是應用停住,而是 TCP 的重送等待
- 若停頓時間符合 RTO 的等待形狀,而且看得到
Retransmission,就相當有線索 - TCP timestamps option 是 RTTM 與 PAWS 的機制,可消除重送時 RTT 測量的模糊
- 本案透過啟用 RFC1323 系 timestamp,抑制了 RTO 在過度保守下停留的時間
想避免的做法:
- 光靠應用日誌判案
- 只看 OS 設定,不看實際封包
- 以為開了 timestamp 連遺失原因也一起消失
實務上有效的做法:
- 先看 wire
- 確認重送與等待時間的形狀
- 確認 TSopt 的協商
- 改善之後也要分別處理遺失源與應用設計
也就是說,這類故障「先猜在哪裡等」比「先想怎麼變快」重要。只要這裡沒偏,調查就能短上許多。
10. 參考資料
- RFC 1323 - TCP Extensions for High Performance
- RFC 7323 - TCP Extensions for High Performance
- RFC 5681 - TCP Congestion Control
- RFC 6298 - Computing TCP’s Retransmission Timer
-
[Description of Windows TCP features - Windows Server Microsoft Learn](https://learn.microsoft.com/en-us/troubleshoot/windows-server/networking/description-tcp-features)
相關文章
共用相同標籤的最新文章。能以相近的主題延伸理解。
工業相機控制應用跑一個月後突然崩潰時(後篇) - 什麼是 Application Verifier 與異常系測試基盤的做法
後篇整理 Application Verifier 是什麼以及怎麼把它編進 Windows 異常系測試基盤。用 Handles 抓 invalid handle、Low Resource Simulation 不弄掛機器就觸發資源不足,搭配 harness EXE 與自家日...
工業相機控制應用跑一個月後突然崩潰時(前篇) - handle leak 的找法與長時間運轉用的日誌設計
本文以工業相機控制應用連續運轉約一個月後突然崩潰的案例,整理 handle leak 的特徵、與記憶體流失的差異,以及如何用 Handle Count 斜率與 create/close 配對日誌追出真正洩漏的位置。讀者可學到把長時間運轉的故障切成可觀測形式,並用短迴圈反覆壓...
Windows 應用的 crash dump 收集入門 - 先搞清楚 WER / ProcDump / WinDbg 怎麼分工
本文整理在 Windows 應用追難以重現的 crash 時,要先以 WER LocalDumps 應用單位設定為起手式,再依現場狀況追加 ProcDump,最後才考慮 MiniDumpWriteDump 自製收集的決策順序。讀完能理解 mini 與 full dump 的...
Windows 中 NIC 詳細設定一次整理 - Jumbo Packet、RSS、LSO、RSC、Flow Control、EEE、Wake on LAN 到底要怎麼設
本文以實務角度逐項拆解 Windows NIC 進階設定,從 Speed & Duplex、Jumbo Packet 到 RSS、RSC、LSO、Interrupt Moderation、Flow Control、EEE 與 Wake on LAN,說明每項調整在吞吐量、延...
開發 COM 元件、OCX/ActiveX 時常見的坑 - 整理 Visual Studio 的 32bit/64bit、註冊、管理員權限
整理開發 COM、OCX、ActiveX 元件時最容易卡關的四個面向:宿主行程的 32bit/64bit、Visual Studio 2022 變成 64bit 後的設計時整合、regsvr32 與 Regasm 的註冊位置、以及管理員權限與 HKCU/HKLM 的關係,協...
相關主題
與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。
Windows 技術主題
彙整 KomuraSoft LLC 關於 Windows 開發、故障調查與既有資產活用文章的主題中心。
故障調查 & 長期運行故障
整理間歇性故障、通訊診斷、長期運行當機、失敗路徑測試基礎的主題頁面。
與本主題相關的服務
本文連結到以下服務頁面,歡迎從最接近的入口查看。
Windows 應用程式開發
支援包含常駐處理、設備連動、運作日誌與可維護結構的 Windows 桌面應用程式。
故障調查 & 根本原因分析
調查難以重現的故障、長時間執行後的問題、記憶體洩漏、通訊停滯等棘手的正式環境問題。
作者檔案
本文作者的個人檔案頁面。
Go Komura
小村軟體有限公司 代表
以 Windows 軟體開發、技術諮詢與故障調查為中心,在難以重現的故障調查與既有資產仍在運作的專案上具有優勢。