2010-11-23 72 views
9

因此,它似乎是一個阻塞read()方法可以返回它完成接收的所有數據被髮送給它之前。接下來,我們將Read()與由來自相關流中的DataAvailable值控制的循環一起包裝。問題是,在這個while循環中你可以接收更多的數據,但是沒有幕後處理讓系統知道這一點。我在網上發現的大多數解決方案都不適用於我。TcpClient.GetStream()。DataAvailable返回false,但流具有更多的數據

我已經落得這樣做是因爲我在循環中的最後一步,我從流中讀取每個塊後做了一個簡單的Thread.Sleep(1)。這似乎給系統更新時間,並且我沒有得到準確的結果,但是這對於解決方案來說似乎有點難以理解,並且相當「有條件」。

這裏是我處理的情況列表:IIS應用程序和獨立的應用程序,無論是用C#編寫的發送之間單一的TCP連接/接收通信。它發送一個請求,然後等待響應。這個請求是由一個HTTP請求發起的,但是我沒有從HTTP請求中讀取數據的問題,這是事實。

下面是用於處理傳入連接的基本代碼

protected void OnClientCommunication(TcpClient oClient) 
{ 
    NetworkStream stream = oClient.GetStream(); 
    MemoryStream msIn = new MemoryStream(); 

    byte[] aMessage = new byte[4096]; 
    int iBytesRead = 0; 

    while (stream.DataAvailable) 
    { 
     int iRead = stream.Read(aMessage, 0, aMessage.Length); 
     iBytesRead += iRead; 
     msIn.Write(aMessage, 0, iRead); 
     Thread.Sleep(1); 
    } 
    MemoryStream msOut = new MemoryStream(); 

    // .. Do some processing adding data to the msOut stream 

    msOut.WriteTo(stream); 
    stream.Flush(); 

    oClient.Close(); 
} 

一個更好的解決方案或只是需要給該睡眠(1)一去,讓事情順利更新一個豎起大拇指的所有反饋歡迎在我們檢查DataAvailable值之前。

猜猜我是2年後希望回答this question也不怎麼東西還都是:)

+0

你最終實現了什麼?你是否堅持線程睡眠?我有一個類似的問題:http://stackoverflow.com/questions/36731247/c-sharp-networkstream-dataavailable-seems-to-be-unreliable – joelc 2016-04-19 23:39:30

+1

是的,這只是這些圖書館的工作方式。他們需要時間運行以完全驗證數據傳入。 – James 2016-04-20 05:24:23

回答

7

我看到這個問題。
您期望通信將比while()循環更快,這是不太可能的。
只要沒有更多數據,while()循環就會結束,在退出後幾毫秒就不會出現這種情況。

你希望有一定量的字節?
OnClientCommunication()多久發一次?誰觸發它?

你怎麼用while()循環後的數據呢?你是否繼續追加到以前的數據?

DataAvailableWILL返回false,因爲你正在閱讀比通信速度更快,所以這是精唯一,如果你經常回來看看這個代碼塊來處理更多的數據來。

+0

你提到的問題正是我的問題,我正在尋找比Thread.Sleep(1)內的while循環更好的解決方案:)我不期待一定數量的數據,超過40KB足以導致它跳過其餘的數據。 OnClientCommunication在執行時會經常被一個IIS Web應用程序調用。這旨在成爲一筆交易,讀取所有數據,處理它,返回結果。 – James 2010-11-23 22:19:48

+0

好吧,它已經有一年和1000次沒有更新的觀點,所以我只是接受這個答案。 – James 2012-01-18 01:11:34

7

你必須知道多少你需要閱讀的數據;你不能簡單地循環讀取數據,直到沒有更多的數據,因爲你永遠不能確定沒有更多的數據會來。

這就是爲什麼HTTP GET結果在HTTP頭一個字節計數:所以當它接收到所有的數據在客戶端就知道了。

這裏有兩個解決方案,您取決於你是否有過控制什麼另一方則發送:

  1. 使用「框架」字:(SB)數據(EB),其中SB和EB是開始塊和結束塊字符(您選擇的),但不能在數據內發生。當你「看到」EB時,你知道你已經完成了。

  2. 在每條消息前面實現一個長度字段,以指示有多少數據:(len)數據。讀取(len),然後讀取(len)字節;必要時重複。

這不像從文件中讀取其中一個零長度讀出裝置結束數據(即是否意味着對方已經斷開,但是另一回事)。

第三個(不推薦)解決方案是您可以實現一個計時器。 一旦你開始獲取數據,設置計時器。如果接收環路在一段時間內處於空閒狀態(如幾秒鐘,如果數據不經常出現),那麼可以假設沒有更多的數據即將到來。最後一種方法是最後的手段......它不是很可靠,很難調整,而且很脆弱。

2

我試圖檢查DataAvailable之前從網絡流中讀取數據,它會返回false,雖然讀取一個字節後它將返回true。所以我檢查了MSDN文檔,他們也在檢查之前閱讀。我會將while循環重新安排到do while循環以遵循此模式。

http://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.dataavailable.aspx

 // Check to see if this NetworkStream is readable. 
     if(myNetworkStream.CanRead){ 
      byte[] myReadBuffer = new byte[1024]; 
      StringBuilder myCompleteMessage = new StringBuilder(); 
      int numberOfBytesRead = 0; 

      // Incoming message may be larger than the buffer size. 
      do{ 
       numberOfBytesRead = myNetworkStream.Read(myReadBuffer, 0, myReadBuffer.Length); 

       myCompleteMessage.AppendFormat("{0}", Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead)); 

      } 
      while(myNetworkStream.DataAvailable); 

      // Print out the received message to the console. 
      Console.WriteLine("You received the following message : " + 
             myCompleteMessage); 
     } 
     else{ 
      Console.WriteLine("Sorry. You cannot read from this NetworkStream."); 
     } 
1

當我有這樣的代碼:

var readBuffer = new byte[1024]; 
    using (var memoryStream = new MemoryStream()) 
    { 
     do 
     { 
      int numberOfBytesRead = networkStream.Read(readBuffer, 0, readBuffer.Length); 
      memoryStream.Write(readBuffer, 0, numberOfBytesRead); 
     } 
     while (networkStream.DataAvailable); 
    } 

從我可以觀察到:

  • 當發送者發送1000個字節和讀者想閱讀。然後我懷疑NetworkStream以某種方式「知道」它應該接收1000個字節。
  • 當我調用.Read之前,任何數據從NetworkStream到達然後.Read應該被阻止,直到它得到超過0字節(或更多,如果.NoDelay是false在networkStream)
  • 然後,當我讀第一批數據I懷疑.Read以某種方式更新NetworkStream中這1000個字節的計數器,在此之前我懷疑,在這段時間內,.DataAvailable被設置爲false,並且在計數器更新後,然後將.DataAvailable設置爲如果計數器數據小於1000字節,則爲正確值。當你考慮它時它是有道理的。因爲否則它將進入下一個週期,然後檢查是否有1000個字節到達,並且.Read方法將無限期阻止,因爲讀取器可能已經讀取了1000個字節,並且不會有更多數據到達。
  • 這個我覺得是失敗的這裏已經點詹姆斯說:

是的,這只是這些庫的工作方式。他們需要時間運行以完全驗證數據傳入。 - 詹姆斯四月在5:24

  • 我懷疑.Read的端部和之間的訪問.DataAvailable前內部計數器的更新不是作爲原子操作(事務),以使20 '16的TcpClient需要更多的時間正確設置DataAvailable。

當我有這樣的代碼:

var readBuffer = new byte[1024]; 
    using (var memoryStream = new MemoryStream()) 
    { 
     do 
     { 
      int numberOfBytesRead = networkStream.Read(readBuffer, 0, readBuffer.Length); 
      memoryStream.Write(readBuffer, 0, numberOfBytesRead); 

      if (!networkStream.DataAvailable) 
       System.Threading.Thread.Sleep(1); //Or 50 for non-believers ;) 
     } 
     while (networkStream.DataAvailable); 
    } 

隨後的NetworkStream有足夠的時間來正確設置.DataAvailable這種方法應該正常運行。

有趣的事實......這似乎是某種程度上取決於操作系統版本。因爲沒有睡眠的第一個功能在Win XP和Win 10上爲我工作,但在Win 7上沒有收到整個1000字節。不要問我爲什麼,但是我測試的很徹底,而且很容易重現。