我需要从NetworkStream
读取,它会随机发送数据,数据包的大小也会不断变化。我正在实现一个多线程应用程序,其中每个线程都有自己的流来读取。如果流上没有数据,应用程序应该一直等待数据到达。但是,如果服务器完成了发送数据并终止了会话,那么它应该退出。
最初我使用Read
方法从流中获取数据,但它曾用于阻塞线程并一直等待直到数据出现在流上。
MSDN上的文档建议
如果没有可供阅读的数据, Read方法返回0.如果 远程主机关闭连接, 并且所有可用数据都已存在 收到后,Read方法完成 立即返回零字节。
但就我而言,我从来没有让Read
方法返回0并优雅地退出。它只是无限期地等待。
在我的进一步调查中,我遇到了BeginRead
,它监视流并在收到数据后立即异步调用回调方法。我也试图使用这种方法寻找各种实现,但是,我无法确定何时使用BeginRead
是有益的而不是Read
。
在我看来,BeginRead
具有异步调用的优势,它不会阻止当前线程。但是在我的应用程序中,我已经有了一个单独的线程来读取和处理来自流的数据,所以这对我来说没什么大不了的。
任何人都可以帮我理解等待和退出机制
BeginRead
以及它与Read
有什么不同?
实现所需功能的最佳方法是什么?
答案 0 :(得分:12)
我使用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.");
}
}
}
答案 1 :(得分:4)
异步I / O可用于在较少的线程中实现相同数量的I / O.
正如您所知,现在您的应用每个流都有一个主题。这对于少量连接是可以的,但是如果你需要一次支持10000怎么办?使用异步I / O,不再需要这样做,因为读取完成回调允许传递上下文来标识相关流。您的读取不再阻塞,因此每个Stream不需要一个线程。
无论您使用同步还是异步I / O,都有一种方法可以检测和处理相关API返回码上的流关闭。如果套接字已经关闭,BeginRead将因IOException而失败。异步读取待处理时关闭将触发回调,EndRead
将告诉您播放状态。
当您的应用程序调用BeginRead时, 系统将等待数据 收到或发生错误,然后 系统将使用单独的线程 执行指定的回调 方法和EndRead上的块直到 提供的NetworkStream读取数据 或抛出异常。
答案 2 :(得分:0)
BeginRead是一个异步进程,这意味着你的主线程将在另一个进程中开始执行Read。所以现在我们有两个并行的过程。如果你想得到结果,你必须调用EndRead,它将给出结果。
一些psudo
BeginRead()
//...do something in main object while result is fetching in another thread
var result = EndRead();
但是如果您的主线程没有其他任何事情可做,而且您需要结果,那么您应该调用Read。
答案 3 :(得分:0)
您是否尝试过server.ReceiveTimeout?您可以设置Read()功能在返回零之前等待输入数据的时间。在您的情况下,此属性可能在某处设置为无限。