2015-10-06 88 views
1

在TCP連接的客戶端,我試圖儘可能重用已建立的連接,以避免每次需要連接時撥打的開銷。從根本上說,它是連接池,儘管從技術上講,我的池大小恰好是一個。如何查詢TCP連接狀態?

我遇到了一個問題,如果一個連接閒置足夠長的時間,另一端斷開連接。我試過使用類似以下的東西來保持連接活着:

err = conn.(*net.TCPConn).SetKeepAlive(true) 
if err != nil { 
    fmt.Println(err) 
    return 
} 
err = conn.(*net.TCPConn).SetKeepAlivePeriod(30*time.Second) 
if err != nil { 
    fmt.Println(err) 
    return 
} 

但是這沒有幫助。事實上,這導致我的關係更快關閉。我很肯定這是因爲(在Mac上),這意味着連接健康狀況在30秒後開始探測,然後以30秒的間隔探測8次。 服務器端不能支持keepalive,所以在4分30秒後,客戶端正在斷開連接。

我可以做任何事情來保持空閒連接無限期地活着,如果有一些方法可以讓我至少檢測到連接已關閉,以便我可以無縫地替換它與一個新的。唉,即使在閱讀完所有文檔並搜索blogosphere尋求幫助之後,我仍然無法找到任何方式來查詢TCP連接的狀態。

必須有一種方法。有沒有人對如何實現這一目標有所瞭解?非常感謝任何人!

編輯:

理想情況下,我想學習如何處理這個問題,低水平,而無需使用第三方庫來完成這個純go--。當然,如果有一些圖書館這樣做,我不介意指出它的方向,所以我可以看到他們是如何做到的。

+0

也許我需要繼續並寫入連接,然後捕獲並分析錯誤,以查看是否建議再次重撥和寫入? –

+0

您通過讀取而不是寫入來檢測到一個關閉的tcp連接。這在任何語言中都是一樣的,因爲它是基礎Berkeley套接字API的工作原理。 – JimB

回答

2

套接字API不允許您訪問連接狀態。您可以通過各種方式從內核查詢當前狀態(例如,Linux上的/proc/net/tcp[6]),但不能保證進一步發送將會成功。

我在這裏有點困惑。我的客戶只發送數據。除了確認數據包之外,服務器不會發回任何內容。閱讀似乎不是確定連接狀態的適當方式,因爲沒有閱讀。

套接字API被定義爲您通過讀取返回0字節來檢測到一個關閉的連接。這是它的工作方式。在Go中,這被翻譯成Read返回io.EOF。這通常是檢測斷開連接的最快方式。

所以,我應該只是發送和採取行動,無論發生什麼錯誤?如果是這樣,那是一個問題,因爲我注意到當我試圖發送一個破損的管道時通常不會發生任何錯誤 - 這看起來完全錯誤

如果仔細研究TCP的工作原理,這是預期的行爲。如果連接在遠程端關閉,那麼您的第一次發送將從服務器觸發RST,完全關閉本地連接。您需要從連接中讀取以檢測關閉,或者如果您嘗試再次發送,則會收到錯誤消息(假設您已經等待了足夠長的時間以使數據包能夠往返),如Linux上的「斷開的管道」 。

澄清...我可以撥號,拔出以太網電纜,然後發送沒有錯誤。這些消息顯然沒有通過,但我沒有收到任何錯誤

如果連接實際上中斷,或者服務器完全沒有響應,那麼您將數據包發送到任何地方。 TCP協議棧無法區分數據包之間的差異,確實是數據包緩慢,數據包丟失,擁塞或連接斷開。系統需要等待重傳超時,並且在發生故障前多次重試數據包。單獨重試的標準配置可能需要13到30分鐘纔會觸發錯誤。

什麼,你可以在你的代碼做的是

  • 打開存活。這會更快地通知您斷開的連接,因爲空閒連接總是被測試。
  • 從插座讀取。或者有一個並行讀取正在進行,或者通過select/poll/epoll檢查首先要讀取的東西(Go通常使用第一個)
  • 設置超時(Go中的最後期限)。

如果您不希望連接中有任何數據,那麼在Go中檢查關閉的連接是非常容易的;調度一個goroutine從連接中讀取,直到出現錯誤。

notify := make(chan error) 

go func() { 
    buf := make([]byte, 1024) 
    for { 
     n, err := conn.Read(buf) 
     if err != nil { 
      notify <- err 
      return 
     } 
     if n > 0 { 
      fmt.Println("unexpected data: %s", buf[:n]) 
     } 
    } 
}() 
+0

我真的很想說謝謝你的幫助。你已經做了很多工作來提高我對所有這些工作的理解。我真的認爲你正朝着正確的方向引導我。說完所有這些,你給出的代碼片段似乎只能檢測到另一端掛斷的情況。這不是我關心的問題,因爲它是一個干擾連接的網絡問題。例如,拔掉網線,不會導致上述代碼檢測到連接不再可用。有什麼想法嗎? –

+0

@KentRancourt:我不確定我還可以添加什麼; **這是如何tcp作品**。如果出現網絡問題,tcp旨在接受您的數據,並不斷嘗試將其發送到目的地。如果您想要將數據發送到服務器,並確保它已被處理,您必須擁有應用程序級別的確認。如果你有這個問題,那麼你可以指定一個時間限制,然後宣佈連接斷開。改變你的協議,你只能盡最大努力發送數據包,並希望他們到達。 – JimB

+0

再次感謝。我接受你的答案。你一直非常有幫助。 fwiw,我沒有控制連接的遠程端,所以發送應用級別的確認是不可能的。最後一個問題是:如果這一切都按照你所描述的那樣工作(並且我毫不懷疑它的確如此),並且在沒有接收到許多分鐘的重試和確認之後,斷開的連接實際上是不可檢測的,這意味着發送者可以將分組乙醚幾分鐘。這在某些方面是否與保證交付的概念相矛盾?有沒有辦法知道一個數據包無法傳送? –

2
  • 在設計上沒有'TCP連接狀態'這樣的東西。只有當你發送內容時會發生什麼。在任何層面上都沒有TCP API,它會告訴你TCP連接的當前狀態。你必須嘗試使用​​它。

  • 如果您發送保持活動探測,服務器沒有任何選擇,只能作出適當的響應。服務器甚至不知道它們是Keepalive。他們不是。它們只是重複的ACK。支持keepalive只是意味着支持發送 Keepalive。

+0

感謝您的回覆。例如,如果沒有辦法查詢連接狀態,netstat是如何向我顯示連接狀態(如ESTABLISHED或CLOSE_WAIT)的? –

+0

@KentRancourt:你可以從內核獲取這些信息。在linux上,netstat解析'/ proc/net/tcp [6]'。儘管如此,你在程序中並不擅長,因爲只要嘗試使用套接字,狀態就會改變。如果您想查看是否有數據或關閉了數據,您可以「接收」;如果您想查看網絡是否響應,則發送;如果您不希望其中任何一方採取未確定數量的時間。 – JimB

+0

@JimB感謝您的指點。我在這裏有一點困惑。我的客戶只發送數據。除了確認數據包之外,服務器不會發回任何內容。閱讀似乎不是確定連接狀態的適當方式,因爲沒有閱讀。那麼我是否應該發送並根據發生的任何錯誤採取行動?如果是這樣,那就是一個問題,因爲我在觀察我試圖發送一個破損的管道時通常不會有任何錯誤 - 這看起來完全錯誤。 –