2010-12-08 29 views
7

我需要從NetworkStream讀取隨機發送數據,並且數據包的大小也不斷變化。我正在實現一個多線程應用程序,其中每個線程都有自己的流讀取。如果流中沒有數據,應用程序應該等待數據到達。但是,如果服務器完成發送數據並終止了會話,則應該退出。NetworkStream.Read()和NetworkStream.BeginRead()之間的區別?

最初我使用Read方法從流中獲取數據,但它用於阻塞線程並一直等待,直到數據出現在流中。

在MSDN文檔建議,

如果沒有數據可供讀取,如果 遠程主機關閉連接, 和所有可用數據已經收到 的 Read方法返回0。 ,Read方法立即完成 並返回零字節。

但在我的情況下,我從來沒有Read方法返回0並正常退出。它只是無限期地等待。

在我的進一步調查中,我遇到了BeginRead,它觀察到流,並在接收到數據後立即調用回調方法。我也嘗試過使用這種方法尋找各種實現方法,但是,我無法確定何時使用BeginRead有益於Read

正如我所看到的,BeginRead只是具有異步調用的優勢,它不會阻塞當前線程。但在我的應用程序中,我已經有一個單獨的線程來讀取和處理來自數據流的數據,所以這對我來說沒有多大的影響。

  • 誰能幫我瞭解 BeginRead等待和退出機制以及它是如何從不同Read

  • 什麼是實現所需功能的最佳方式?

+0

您確定遠程端正在正確關閉連接嗎?我從來沒有遇到過`Read`沒有回覆的問題。否則你的方法似乎是正確的方向。 – 2010-12-08 15:02:42

+0

@Matthew好吧,說實話,我無法肯定地說。它是我們正在閱讀的第三方服務。他們已經指定了關機時間,我們假設它發生在前面提到的情況。我只想先檢查並重新檢查我的結局,然後再向他們舉起一面旗幟。 – 2010-12-09 05:55:27

回答

10

我用BeginRead,但繼續阻止使用WaitHandle螺紋:

byte[] readBuffer = new byte[32]; 
var asyncReader = stream.BeginRead(readBuffer, 0, readBuffer.Length, 
    null, null); 

WaitHandle handle = asyncReader.AsyncWaitHandle; 

// Give the reader 2seconds to respond with a value 
bool completed = handle.WaitOne(2000, false); 
if (completed) 
{ 
    int bytesRead = stream.EndRead(asyncReader); 

    StringBuilder message = new StringBuilder(); 
    message.Append(Encoding.ASCII.GetString(readBuffer, 0, bytesRead)); 
} 

基本上它允許異步的超時使用WaitHandle讀取併爲您提供了一個布爾值(completed)如果讀取在設定的時間內完成(在這種情況下爲2000)。

這裏的複製,並從我的Windows Mobile項目之一粘貼我的全碼流閱讀代碼:

private static bool GetResponse(NetworkStream stream, out string response) 
{ 
    byte[] readBuffer = new byte[32]; 
    var asyncReader = stream.BeginRead(readBuffer, 0, readBuffer.Length, null, null); 
    WaitHandle handle = asyncReader.AsyncWaitHandle; 

    // Give the reader 2seconds to respond with a value 
    bool completed = handle.WaitOne(2000, false); 
    if (completed) 
    { 
     int bytesRead = stream.EndRead(asyncReader); 

     StringBuilder message = new StringBuilder(); 
     message.Append(Encoding.ASCII.GetString(readBuffer, 0, bytesRead)); 

     if (bytesRead == readBuffer.Length) 
     { 
      // There's possibly more than 32 bytes to read, so get the next 
      // section of the response 
      string continuedResponse; 
      if (GetResponse(stream, out continuedResponse)) 
      { 
       message.Append(continuedResponse); 
      } 
     } 

     response = message.ToString(); 
     return true; 
    } 
    else 
    { 
     int bytesRead = stream.EndRead(asyncReader); 
     if (bytesRead == 0) 
     { 
      // 0 bytes were returned, so the read has finished 
      response = string.Empty; 
      return true; 
     } 
     else 
     { 
      throw new TimeoutException(
       "The device failed to read in an appropriate amount of time."); 
     } 
    } 
} 
+0

謝謝@GenericTypeTea,這看起來像一個有趣的方法,但是我無法確定正確的超時期限。儘管如此,我認爲這可以稍微修改一下,以解決我的問題。如果成功,我會嘗試並報告。順便說一句,你的句柄讓我微笑...... :) – 2010-12-09 04:30:18

+1

如果我沒有弄錯,你必須在每次BeginABC調用時總是調用EndABC。在上面的代碼中,如果有超時,End從不會被調用。調用End(並處理任何可能的異常)的回調可能應該添加到上面的代碼中。 – SpeksETC 2010-12-09 05:25:48

4

異步I/O可用於實現在更短的線程的I/O相同的金額。

如您所知,現在您的應用程序每個流都有一個線程。對於少量的連接,這是可以的,但是如果你需要一次支持10000,那該怎麼辦?使用異步I/O,這不再是必需的,因爲讀取完成回調允許上下文通過,以識別相關流。你的讀取不再阻塞,所以你不需要每個流一個線程。

無論您使用同步還是異步I/O,都有一種方法可以檢測並處理相關API返回碼上的流關閉。如果套接字已關閉,則BeginRead應該以IOException失敗。當異步讀取處於待處理狀態時關閉,將觸發回調,然後EndRead會告訴你遊戲狀態。

當應用程序調用的BeginRead, 系統將等待直到數據被接收 或發生錯誤,然後 系統將使用一個單獨的線程 執行指定的回調 方法,和上EndRead塊直到 提供的NetworkStream讀取數據 或引發異常。

0

BeginRead是一個異步進程,這意味着你的主線程將開始執行在另一個進程中讀取。所以現在我們有兩個並行的進程。如果你想得到結果,你必須調用EndRead,它會給出結果。

一些psudo

BeginRead() 
//...do something in main object while result is fetching in another thread 
var result = EndRead(); 

,但如果你的主線程沒有別的做,ü有需要的結果,U應該叫閱讀。

0

你試過server.ReceiveTimeout嗎?您可以設置Read()functon在返回零之前等待數據傳入的時間。在你的情況下,這個屬性可能設置爲無限的地方。