← 所有文章

FIELD NOTES / 工業通訊與網路

TCP連線成功但應用無回覆如何分層定位

工業通訊與網路作者:站長預估 6 分鐘閱讀

把TCP問題分成connect、完整send、peer應用處理與完整response,分別設timeout,使用兩端日誌和封包證據定位,避免把ACK當成應用完成。

本文目錄

先拆開四個不同的完成狀態

TCP connect完成,只表示本端完成了傳輸連線建立;它不表示請求已完整送出,也不表示對端應用程式已解析、執行或回覆。排查時先把事件拆成connect、send、peer application與response四層,每層有自己的時間點、逾時和證據。

本篇選定自訂文字測試協定:每筆訊息由ASCII文字加一個LF位元組結束,上限64 bytes含LF,不含CR;文字本體不得含LF。請求READ 17後接LF,回覆OK 17 25.3後接LF。這只是離線測試協定,不是Modbus,也不是任何PLC原生指令。

send回傳接受的byte數;若少於請求長度,仍要處理剩餘部分。完整send成功通常只代表資料交給本端socket緩衝,不能當成peer已讀取,更不能當成動作完成。即使收到傳輸層ACK,也只能支持資料在網路堆疊中的某段進度;ACK不證明對端應用已解析請求或已寫入設備。

紀錄至少包含request_id、連線epoch、訊息長度、摘要、connect完成時間、send完成時間、response解析時間、錯誤類型與對端端點。時間要使用單調計時器計算逾時,牆上時鐘只作事件顯示,避免校時讓剩餘時間倒退。

分開connect send與response timeout

connect timeout處理的是連線建立未完成,例如目標不可達、路由或服務未監聽;send timeout處理的是請求尚未完整交給socket,例如寫入阻塞或對端接收速度不足;response timeout則是請求送完後,未在期限內得到完整且可解析的應用回覆。三者原因和重試策略不能混成一個timeout。

假設t=120毫秒連線完成,8 bytes請求先由send接受4 bytes,t=370毫秒才接受完整8 bytes。對端回覆OK 17 25.3共10 bytes但缺LF,本端已有資料,卻不能交給完整訊息解析。紀錄應分別呈現發送耗時250毫秒與回覆缺終止符,不能只寫TCP連不上。

接收端累積byte直到LF,才取出一筆訊息;剩餘byte保留給下一筆。半筆回覆不能因暫時沒新資料就算完成,一次read收到兩筆也要拆開。若檢查超過64 bytes仍未見LF,回報ProtocolError,依本例契約關閉這條測試連線。

TLS錯埠可能在TCP上連得通卻拿到非TLS內容;格式錯可能是雙方協定版本不同;缺少terminator則可能一直等到response timeout。保存TLS協商結果、首段封包摘要和解析狀態,能把傳輸建立成功與應用協定失敗分開。

明訂兩個期限:例如請求送完後最多一秒收齊回覆,且整個交易自開始起最多兩秒。每次收到零星資料不能無限延後總期限。保存已送、已收byte數和是否見到LF,逾時時才知道卡在傳送、等待首byte,還是等尾端。

用兩端日誌與封包證據定位

先用request_id和epoch對齊本端與對端日誌:本端若只有connect而沒有完整send,查寫入流程;兩端都顯示送完但對端沒有解析事件,查封包邊界、TLS、服務埠和版本;對端顯示已處理但本端沒回覆,查回送路徑、response framing和讀取狀態。

封包擷取應配合授權與敏感資料遮罩,記錄序號、方向、長度與時間,不必把秘密內容寫入日誌。若只能看到TCP ACK,證據等級仍低於看到完整應用回覆。對端應記錄「收到完整請求」「通過解析」「開始處理」「完成或拒絕」等事件,避免只寫一行connected。

正常結果範例是:connect 10:00:00.120、send完整 10:00:00.370、peer parsed 10:00:00.381、response完整 10:00:00.402。失敗結果若停在parsed之前,就不要把設備未動作推給PLC;先找協定或服務流程。每筆事件要附同一request_id,防止兩次請求混看。

生產環境禁止看到response timeout就盲目重試write。原請求可能已在對端完成,只是回覆遺失;重試可能造成重複動作。若業務需要重試,必須有對端可查詢的冪等識別、明確結果查詢或人工確認,且依設備風險另訂策略。

驗收向量與適用限制

離線驗收至少測:無服務、connect延遲、部分send、完整send但對端不解析、TLS錯埠、格式版本錯、缺terminator、回覆分段、多回覆黏在一次讀取,以及對端已完成但回覆遺失。每列標出四層狀態和應保存的證據。

若另一套協定使用ACK再回結果,必須另外定義ACK是收到、排隊或完成。本例只接受包含同一請求識別的完整OK回覆;收到ACK字串既不符合格式,也不能證明讀取已完成。別把其他協定的成功字樣套進本例。

TCP保證的是有序可靠位元組流的傳送語意範圍,不保證應用訊息邊界與製程動作結果。實際socket選項、TLS函式庫、Q系列PLC通訊模組和掃描行為都要查各自手冊。本文是協定排查教學。

完成排查後,報告應寫出最後已證實的層級,例如「connect完成、send完整、peer未見parsed、response無資料」,並列下一個證據,而不是籠統說網路正常或PLC無回應。

如果服務同時支援查詢與寫入,兩者的回覆格式和完成語意要分開。查詢回覆可以確認目前狀態,但不能自動證明先前寫入一定由本次請求完成;結果中應帶request_id或服務端序號,才能做可信的關聯。

逾時分類也要保存最後讀寫進度,例如已送位元組、已收位元組和預期總長度。這些數字能快速指出是發送卡住、對端未回覆,還是只差終止符。重新連線前封存該次證據,避免新連線的空白紀錄覆蓋原始問題。

這種分層報告也讓交接更可靠,下一位工程師能從證據接續,不必重猜整條連線。

常見問題

對端日誌若只有connected,應補上應用層事件;本端若沒有request_id,先修正可觀測性再調整timeout。沒有足夠證據時,結論應保持在已知層級,不把推測寫成設備故障。

兩端使用同一請求識別17核對,但時間戳來自不同主機時,不直接相減成網路延遲。先確認時鐘品質;各端耗時用本機單調時鐘計算。若伺服器日誌缺失,只能說尚無解析證據,不能證明它完全沒執行。

問:connect成功就能直接判斷服務正常嗎?答:不能;還要證明完整send、對端解析處理與完整response。

問:TCP ACK代表應用已完成嗎?答:不代表;ACK只提供傳輸層進度證據,不能證明peer app已執行。

問:缺少LF是否等於對端沒有傳資料?答:不等於。可能已收到10 bytes文字但未形成完整訊息;應記已收長度及等待LF,不混成完全無資料。

問:response timeout後一定重試write嗎?答:不應盲重試;先考慮動作可能已完成,使用冪等識別或結果查詢。

參考:IETF RFC 9293 TCP規範,作為TCP連線與可靠位元組流語意的協定參考;本文應用完成判斷需由自訂協定另行定義。

參考:IETF RFC 1122主機通信層需求,作為TCP主機行為與逾時排查的背景資料;不代表任何PLC API。

延伸閱讀