如何等待网络流有数据可读?

9
我在应用程序中有一个工作线程,负责处理三个不同的任务。其中两个任务的请求出现在我编写的队列中,另一个任务则在网络流上出现请求时激活。当没有工作需要完成时,我希望我的工作线程等待。这对于两个队列很容易实现,因为它们公开了一个 ManualResetEvent,在它们有项目时被设置,然而 NetworkStream 似乎没有这个功能。该 NetworkStream 是从 TcpClient 检索到的。
我需要的代码看起来像这样:
while (notDone)
{
    WaitHandle.WaitAny(new WaitHandle[] { queue1.HasData, queue2.HasData, netStream.HasData } );
    // ...
    if (netStream.DataAvailable)
    {
        netStream.Read(buffer, 0, 20);
        // process buffer
    }
}

有没有人知道一种获取WaitHandle的方法,当NetworkStream有数据时设置它?

3个回答

7
您可以使用 NetworkStream 的异步方法,并在 EndReceive 方法中设置 ManualResetEvent。
// ...
netStream.BeginRead(buffer, offset, callback, state);
// ...

在回调方法内部

netStream.EndRead(ar);
netStreamManualResetEvent.Set();

然后是你的代码

while (notDone)
{
    WaitHandle.WaitAny(new WaitHandle[] { queue1.HasData, queue2.HasData, netStreamManualResetEvent} );
    // ...
    if (netStream.DataAvailable)
    {
        // make the buffer from the AsyncState in the callback method available here
        // process buffer
    }
}

如果远程主机关闭连接并且没有更多数据可用,则将读取0字节。你为什么问? - HasaniH
如果你不能让BeginRead读取零字节,那么你就需要在主循环和回调之间管理读取缓冲区。这是完全可行的,但并不像我希望的那样好。 - Martin Brown
不一定是因为缓冲区将保持不变(由于流)直到您在回调中调用EndRead后再次调用BeginRead,这意味着您可以通过适当的BeginRead和EndRead调用来管理缓冲区,例如在回调中不要再次调用BeginRead,而是在处理缓冲区代码后再次调用它。 - HasaniH

2
最简单的方法可能是使用一个额外的线程,同步读取并将额外的数据放入另一个队列中。
或者您可以使用异步IO,但这有点棘手 - 仍然需要有一些额外的队列。
虽然Socket有一个Select()方法(而且您可以从NetworkStream中获取套接字),但我认为它没有以一种让您将其与其他类型的等待句柄混合使用的方式公开此功能。

0
我发现,如果你使用 size = 0 调用 NetworkStream.Read,它实际上会阻塞:
networkstream.Read(buffer, 0, 0);

文档并不是很清楚,但对我来说它起作用了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接