C# NetworkStream.Read()和NetworkStream.BeginRead()之间的区别?
我需要读取C# NetworkStream.Read()和NetworkStream.BeginRead()之间的区别?,c#,.net,multithreading,networkstream,C#,.net,Multithreading,Networkstream,我需要读取NetworkStream,它会随机发送数据,数据包的大小也会不断变化。我正在实现一个多线程应用程序,其中每个线程都有自己的读取流。如果流中没有数据,应用程序应该一直等待数据到达。但是,如果服务器发送完数据并终止了会话,则应该退出 最初我使用Read方法从流中获取数据,但它用于阻塞线程并一直等待数据出现在流中 MSDN上的文档表明 如果没有可读取的数据, Read方法返回0。如果 远程主机关闭连接, 所有可用的数据都已更新 接收后,读取方法完成 立即返回并返回零字节 但在我的例子中,我
NetworkStream
,它会随机发送数据,数据包的大小也会不断变化。我正在实现一个多线程应用程序,其中每个线程都有自己的读取流。如果流中没有数据,应用程序应该一直等待数据到达。但是,如果服务器发送完数据并终止了会话,则应该退出
最初我使用Read
方法从流中获取数据,但它用于阻塞线程并一直等待数据出现在流中
MSDN上的文档表明
如果没有可读取的数据,
Read方法返回0。如果
远程主机关闭连接,
所有可用的数据都已更新
接收后,读取方法完成
立即返回并返回零字节
但在我的例子中,我从未使用Read
方法返回0并优雅地退出。它只是无限期地等待
在我进一步的调查中,我遇到了BeginRead
,它监视流并在接收到数据时异步调用回调方法。我也尝试过使用这种方法寻找各种实现,但是,我无法确定使用BeginRead
与Read
相比,什么时候会有好处
在我看来,BeginRead
具有异步调用的优点,它不会阻塞当前线程。但在我的应用程序中,我已经有了一个单独的线程来读取和处理流中的数据,所以这对我来说没有多大区别
- 有人能帮我了解一下等待和退出机制吗
它与BeginRead
有何不同Read
- 实现所需功能的最佳方式是什么
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
),则为您提供一个布尔值(completed
)
以下是从我的一个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 正如您所注意到的,现在您的应用程序每个流有一个线程。对于少量的连接,这是可以的,但是如果您需要一次支持10000个,该怎么办?对于异步I/O,这不再是必需的,因为读取完成回调允许传递标识相关流的上下文。读取不再阻塞,因此每个流不需要一个线程 无论您使用同步还是异步I/O,都有一种方法可以检测和处理相关API返回代码上的流关闭。如果套接字已关闭,则应以IOException失败。异步读取挂起时关闭将触发回调,
EndRead
随后将告诉您播放状态
当应用程序调用BeginRead时,
系统将等待数据恢复
收到或发生错误,然后
系统将使用单独的线程
执行指定的回调
方法,并在EndRead上块,直到
提供的网络流读取数据
或者抛出异常
BeginRead是一个异步进程,这意味着您的主线程将在另一个进程中开始执行读取。现在我们有两个并行进程。如果你想得到结果,你必须调用EndRead,它会给出结果 一些假动作
BeginRead()
//...do something in main object while result is fetching in another thread
var result = EndRead();
但是如果你的主线程没有其他事情要做,你需要结果,你应该调用Read。你试过server.ReceiveTimeout吗?您可以设置Read()函数在返回零之前等待输入数据的时间。在您的情况下,此属性可能在某处设置为无限。您确定远程端正确关闭了连接吗?我从未遇到过
Read
不返回的问题。你的方法似乎是正确的方向。@Matthew嗯,老实说,我不能肯定这一点。这是我们阅读的第三方服务。他们已经指定了关闭时间,我们假设它发生在上述情况下。我只想在向他们发出警告之前,先检查再检查。这里涉及多个进程的含义是完全错误的。@Steve,我认为@Bonshington在说“进程”时的意思是“线程”。谢谢@Steve。每个流一个线程是由于许多其他设计考虑因素造成的,这不是一个真正的问题。我们最多会有10个并发线程。谢谢@GenericTypeTea,这看起来是一个有趣的方法,但我无法确定正确的超时时间。尽管如此,我认为这可以稍加修改,以解决我的问题。如果成功,我会尝试并报告。顺便说一句,你的把手让我笑了……)如果我没记错的话,每次打电话给BeginABC,你都必须打电话给EndABC。在上面的代码中,如果有超时,则永远不会调用End。可能应该在上述代码中添加一个调用End(并处理任何可能的异常)的回调。@SpeksETC-我认为您弄错了。我总是调用end:int bytesRead=stream.EndRead(asyncReader);,但也许我应该更改代码,在bool completed=handle.WaitOne(2000,false)行之后直接调用该代码;以消除重复。