2010-08-18 84 views
1

我遇到了Socket.SendAsync方法未檢測到死TCP連接的問題。在我的客戶端/服務器應用程序中,服務器定期向連接的客戶端發送心跳。Socket.SendAsync沒有檢測到死TCP連接

我遇到的問題是,即使客戶端可能已經死亡,SendAsync方法的回調指示「SocketError.Success」,並且Socket.Connected屬性爲true,即使客戶端不再是「活」。所以,對於服務器來說,看起來心跳數據已正確發送並且客戶端仍然活着。

我每次都看到這個問題,客戶端PC要麼進入睡眠/休眠狀態,要麼當客戶端在VMWare實例中運行並且該實例被掛起時。我沒有看到這個問題,當客戶端關閉應用程序,從任務管理器殺死它,等

internal void InternalSendAsync(ByteDataChunk chunk) 
    { 
     asyncSendArgs.SetBuffer(chunk.Buffer, 0, chunk.Offset); 
     asyncSendArgs.UserToken = chunk; 
     Socket.SendAsync(asyncSendArgs); 
    } 

    private void SendCompleted(object sender, SocketAsyncEventArgs args) 
    { 
     if (args.SocketError != SocketError.Success || !Socket.Connected) 
     { 
      InternalDisconnect(args.SocketError); 
      return; 
     } 

     // all is good & do some other stuff 
    } 

任何人有任何想法是怎麼回事,爲什麼SendCompleted方法不返回SocketError即使客戶端已經很長時間了(我已經讓服務器運行了幾個小時,並且死亡套接字從未被檢測到)?

感謝,

湯姆

回答

3

MSDN

注意的 成功完成SendAsync方法並不表示 數據已成功交付 。

海事組織,關於網絡最棘手的部分之一是你不能確定客戶端得到的數據。如果你正在實施心跳系統,你應該讓客戶端迴應心跳,證明它仍然活着。

當您暫停一個進程或休眠計算機時,我認爲如果關閉正在運行的計算機上的套接字,就不會關閉它。

+0

對 - 這是通常的做法。如果客戶端在秒內未對心跳做出響應,則認爲它已經死機並斷開連接。 – caf 2010-08-19 01:25:43

+0

我同意這將是一種方法來做到這一點,但我從來沒有見過這個問題與同步發送,因爲我總是收到發送錯誤後,即使主機進入睡眠/休眠 – TJF 2010-08-19 15:00:48

0

心跳是否實際發送?我的懷疑將是Naggle algorithm。拉出wireshark並檢查線路上的流量。您可以用SocketOptionName.NoDelay禁用Nagle。 From MSDN

BeginSend方法的成功完成意味着底層系統有足夠的空間來緩存網絡發送的數據。 如果您的應用程序立即將每個字節發送到遠程主機很重要,則可以使用SetSocketOption啓用SocketOptionName.NoDelay有關網絡效率緩衝的更多信息,請參閱MSDN中的Nagle算法。
+0

Nagle不應該有甚至在Nagle打開的情況下,數據將在固定的時間間隔後發送(通常爲200-500ms) – TJF 2010-08-19 14:59:27

+0

不知道.net異步層是否會自行緩衝。 – 2010-08-21 15:08:02

0

忽略Socket.Connected屬性;它幾乎沒用。在您的示例代碼中,如果或者Socket.Connected爲真或者沒有錯誤代碼,則認爲一切正常。我要做的第一件事是刪除Socket.Connected部分。

我建議始終保持突出的異步讀取以及定期發送的心跳。如果套接字不再連接,則讀取或寫入都會導致錯誤。

發送必須超時若干次,並具有指數回退。因此,檢測對方何時消失需要一段時間(在程序退出的情況下,操作系統會立即迴應連接不再可用)。儘管如此,它不應該在幾小時之內。最多幾分鐘(假設網絡連接速度較慢)。我的套接字在一秒鐘內定期檢測掉連接。

+0

這正是我正在做的事情,而問題主要集中在如果客戶端機器進入睡眠狀態時寫入不會導致錯誤的原因。我可以通過SendAsync方法向該套接字寫入數個小時,並且如果客戶端被掛起但它確實會拋出錯誤(例如,客戶端死亡 – TJF 2010-08-19 14:58:15

+0

你的代碼假設如果'Socket.Connected'爲真,那麼連接仍然有效。那是錯的。刪除支票的「Socket.Connected」部分(僅留下支票的錯誤部分),然後查看是否有效。 – 2010-08-19 15:07:53

+0

你是對的,我在這裏發佈代碼時犯了一個錯誤,因爲我的生產代碼正在做一些其他的事情,當我縮短它並張貼在這裏時我打錯了這個。生產代碼是|| !Socket.Connected – TJF 2010-08-19 15:17:51

0

您是否使用過Wireshark或類似軟件來查看網絡上發生了什麼?有人會認爲,如果客戶端上的TCP子系統沒有確認數據包,那麼應該有套接字錯誤。也許客戶端保持打開端口並確認數據包。如果是這樣,那麼你可能想嘗試在客戶端解決這個問題,或者做尼古拉所說的。

+0

在捕獲中,我看到一個PSH,ACK,然後是3個重傳,之後沒有任何內容。 Imho,我應該在套接字上收到超時異常,因爲沒有收到ACK,但我不知道? – TJF 2010-08-20 17:19:23