2011-09-27 46 views
0

我有一個數據收集系統,通過TCP連接將數據從收集計算機(服務器)傳遞到繪圖計算機(客戶端)。該代碼在第一次運行集合時工作正常。如果系統未閒置超過三分鐘,代碼將繼續在後續運行中正常工作。如果系統保持空閒超過三分鐘,則傳輸會在前12秒左右停頓(足夠長以致導致收集緩衝區溢出)。如果空閒超過三分鐘,.NET Tcp流失敗

在每個集合開始時,客戶端進入一個循環30秒,每隔1ms查看_clientStream.DataAvailable = true。該循環類似於這樣(一些錯誤檢查代碼中刪除):

public bool WaitForData(int maxWaitInMs) 
    { 
     DateTime start_time = DateTime.Now; 

     while (!_clientStream.DataAvailable) 
     { 
      Thread.Sleep(DATA_WAIT_DELAY); //1 ms 
      TimeSpan elapsed_time = DateTime.Now - start_time; 
      if (elapsed_time.TotalMilliseconds > maxWaitInMs) 
      { 
       return false; 
      } 
     } 
     return true; 
    } 

服務器端只是做固定長度的數據

_dataTcpServer.SendData(packet_buffer, OFFSET, packet.Size()); 

當出現問題的一個簡單的寫,我可以告訴服務器調用SendData一次的調試器返回下一個數據包,然後再次調用send方法,然後在SendData調用中掛起大約12秒鐘。與此同時,客戶端在12秒內沒有看到DataAvailable變爲真。發送超時保留在默認值,因此應該是無限的。第二次的時間似乎與發送的數據量有一定的關係。

我在這個系統中做了一件不同的事情,而不是在客戶端應用程序啓動時創建TcpClient,而是在客戶端需要連接時新建一個TcpClient。斷開連接時,我將通過TcpCLient.GetStream檢索到的流處理掉,然後關閉TcpClient。

我只是希望瞭解爲什麼閒置三分鐘有這種效果(兩個系統都運行XP)。

編輯 - 問題解決了,但理解是難以捉摸

在服務器我用所採用TcpListener.AcceptTcpClient(),以允許客戶端連接一個單獨的線程。 AcceptTcpClient()返回一個TcpClient對象。我會定期檢查TcpClient的IsConnected屬性,以便知道客戶端是否斷開連接(即測量結束),然後再次接受AcceptTcpClient()並等待下一次測量的開始。問題是即使客戶端的TcpClient斷開連接,服務器的TcpClient.IsConnected也總是返回true。

我解決了這個問題,讓客戶端發送消息(不通過TCP)到服務器,然後服務器強制斷開連接。現在一切正常,但我確信我沒有正確使用這個API。儘管我可以看到異步執行接受的方法,但是我沒有看到服務器應該如何發現客戶端何時斷開連接,並且我不明白爲什麼服務器的TcpClient.IsConnected屬性在不再有有效的連接。

此外,我遲到了,注意到ExclusiveAddressUse屬性,所以我從來沒有把它設置爲true。該系統的行爲就像在幾分鐘內另一個客戶端連接是一樣的,同一個連接被重用並且服務器可以向客戶端發送數據(即,就好像客戶端從未斷開連接一樣)。如果斷開連接超過三分鐘後,新的客戶端連接被接受(儘管據我所知,我的代碼永遠不會回到AcceptTcpClient()行),但它不再是服務器的連接發送。

回答

1

雖然我仍然不明白爲什麼我的客戶端可以連接,當我的服務器沒有掛在AcceptTcpClient上我想我明白爲什麼連接返回true,即使客戶端斷開連接。我深入到MSDN上的底層套接字文檔並看到:

連接屬性的值反映了連接狀態和最新操作的狀態。如果您需要確定連接的當前狀態,請進行非阻塞的零字節發送呼叫。

由於客戶端知道要退出是因爲服務器停止發送數據,所以在客戶端退出前最後一次發送成功。我認爲Connected是一個可怕的這個屬性的名稱。它應該是WasConnected或WasConnectedAtLastSend,而更詳細的它會爲我節省很多心痛(因此將有更快閱讀MSDN文檔)。

1

Thread.Sleep在處理IO時是EVIL。如果它仍然不起作用,請使用socket.BeginReceive,然後再回來。

+0

爲什麼Thread.Sleep固有地EVIL?這是與UI分離的線程,系統的其餘部分做的很少。您是否建議使用Thread.Sleep使系統不穩定或影響TCP/IP堆棧的工作方式?如果是這樣,那麼可能解釋了我發現的實際問題。我將用今天的結果更新我的問題。 – Tod

+0

輪詢從來不是一個好主意,因爲它佔據了一個沒有理由的新線程。當然。在客戶端應用程序中正常工作,但不在服務器應用程序中。 – jgauffin

+0

即使這不是我的問題的原因,我很感激您的帖子。我詳細閱讀了異步方法。他們似乎真的只是使用線程池,並據我可以告訴可能做相當於Thread.Sleep(0)我可以看到他們的使用如何可以使我的代碼更清潔(和更高效)。加上您的帖子,我激發了我對ManualResetEvent的學習,並使用它來代替輪詢方法。下一步是直接開始使用ThreadPool。 – Tod