NetworkStream.Read()和NetworkStream.BeginRead()之间的区别?

我需要从NetworkStream读取,它会随机发送数据,数据包的大小也会不断变化。 我正在实现一个multithreading应用程序,其中每个线程都有自己的流来读取。 如果流上没有数据,应用程序应该一直等待数据到达。 但是,如果服务器完成了发送数据并终止了会话,那么它应该退出。

最初我使用了Read方法从流中获取数据,但它曾用于阻塞线程并一直等待直到数据出现在流上。

MSDN上的文档建议,

如果没有可用于读取的数据,则Read方法返回0.如果远程主机关闭连接,并且已收到所有可用数据,则Read方法立即完成并返回零字节。

但就我而言,我从未让Read方法返回0并正常退出。 它只是无限期地等待。

在我的进一步调查中,我遇到了BeginRead ,它BeginRead流并在收到数据后立即异步调用回调方法。 我也试图使用这种方法寻找各种实现,但是,我无法确定何时使用BeginRead是有益的而不是Read

在我看来, BeginRead具有异步调用的优势,它不会阻塞当前线程。 但是在我的应用程序中,我已经有一个单独的线程来读取和处理来自流的数据,所以这对我来说没什么大不了的。

  • 任何人都可以帮我理解BeginRead的等待和退出机制,它与Read有何不同?

  • 实现所需function的最佳方法是什么?

我使用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超时异步读取,如果读取在设置时间内完成(在本例中为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."); } } } 

异步I / O可用于在较少的线程中实现相同数量的I / O.

如您所知,现在您的应用每个Stream有一个主题。 这对于少量连接是可以的,但是如果你需要一次支持10000怎么办? 使用异步I / O,不再需要这样做,因为读取完成回调允许传递标识相关流的上下文。 您的读取不再阻止,因此每个Stream不需要一个线程。

无论您使用同步还是异步I / O,都有一种方法可以检测和处理相关API返回码上的流关闭。 如果套接字已经关闭, BeginRead将因IOException而失败。 当异步读取处于挂起状态时关闭将触发回调,然后EndRead将告诉您播放状态。

当您的应用程序调用BeginRead时,系统将等待直到收到数据或发生错误,然后系统将使用单独的线程执行指定的回调方法,并在EndRead上阻塞,直到提供的NetworkStream读取数据或引发exception。

你尝试过server.ReceiveTimeout吗? 您可以设置Read()functon在返回零之前等待输入数据的时间。 在您的情况下,此属性可能在某处设置为无限。

BeginRead是一个异步进程,这意味着你的主线程将在另一个进程中开始执行Read。 所以现在我们有2个并行进程。 如果你想得到结果,你必须调用EndRead,它将给出结果。

一些psudo

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

但是如果你的主线程没有其他任何事情可做,你必须要求结果,你应该调用Read。