最近在使用 Golang 編寫 TCP 通訊程式時,遇到了一個奇怪的 Close 錯誤,將其記錄下來分享一下。
背景
在我寫的 TCP 通訊程式中,使用了 Go 標準庫中的 net 包,透過 DialTCP 函數連接到伺服器。在通訊結束後,使用 Close 方法關閉連線。在大多數情況下,這個過程都是成功的。但有時候,在呼叫 Close 方法時,會拋出一個奇怪的錯誤,程式崩潰了。
錯誤描述
-Close tcp 呼叫中的錯誤-
描述: reset by peer`
錯誤日誌中顯示,在使用Close 方法關閉連線時,報錯“reset by peer”,也就是伺服器端主動關閉了連線。
排查
第一步,我查看了原始程式碼,確認在呼叫 Close 方法之前,連線並沒有關閉。第二步,我試著從程式碼的角度分析這個問題,接著可以排除程式碼的缺陷。
第三步,我搜尋了一下這個問題,發現了相似的案例。在開源社群 Github 上,有很多人也遇到了類似的問題。有些人認為這是作業系統對 TCP 規範的實作有問題,有些人則認為是網路卡驅動的錯誤。我看到這些資訊之後,稍微查閱了一下關於 TCP 協定的規範,發現對於連線關閉的操作,確實有一個 TIME_WAIT 的時間等待。
具體來說,一個 TCP 連線關閉後,作業系統會等待一定的時間,直到確認這個連線已經完全關閉,才會釋放相關的資源。這個時間通常為 2MSL(Maximum Segment Lifetime) ,在 Linux 上預設為 60 秒。如果在這個等待時間內,有新的相同位址和連接埠的連接請求時,會觸發 RST 段,也就是對方提前關閉連接,此時就會出現「reset by peer」的錯誤。
那麼,這樣的問題該如何解決呢?
解決
對於這個問題,有兩種解決方法:
修改 Linux 核心的參數,可以延長 TIME_WAIT 時間,更長的時間等待可以確保連線被完全關閉。當然,在這個延長的時間內,系統中長時間處於 TIME_WAIT 狀態的連線數量也會增加。
在關閉連線時,開啟 SO_REUSEADDR 選項,使得連線位址可重複使用。這樣,在連線關閉後,下一次新的連線就可以直接復用原先的位址,避免了出現「reset by peer」的錯誤。具體實作方法如下:
conn, err := net.DialTCP("tcp", nil, tcpAddr) err = conn.SetReuseAddr(true) err = conn.Close()
總結
以上就是我在使用 Golang 寫 TCP 通訊程式時,遇到的一個奇怪 Close 錯誤的案例。原因是 TCP 規範中需要等待的 TIME_WAIT 時間,導致連線沒有即時釋放。透過延長等待時間或開啟 SO_REUSEADDR 選項,我們可以避免這類錯誤的發生。同時,這也提醒我們在進行網路程式設計時,需要注意 TCP 規範中的一些細節,以避免不必要的錯誤。
以上是golang tcp close 報錯的詳細內容。更多資訊請關注PHP中文網其他相關文章!