8

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

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

MSDN 上的文档建议,

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

但就我而言,我从来没有得到Read返回 0 并优雅退出的方法。它只是无限期地等待。

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

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

  • 任何人都可以帮助我了解等待和退出机制, BeginRead它有什么不同Read

  • 实现所需功能的最佳方式是什么?

4

4 回答 4

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您提供一个布尔值 ( )。completed2000

这是从我的一个 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.");
        }
    }
}
于 2010-12-08T15:12:10.640 回答
5

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

正如您所注意到的,现在您的应用程序的每个 Stream 都有一个线程。这对于少量连接是可以的,但是如果您需要一次支持 10000 个呢?使用异步 I/O,这不再是必需的,因为读取完成回调允许传递上下文来标识相关流。您的读取不再阻塞,因此每个 Stream 不需要一个线程。

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

当你的应用程序调用 BeginRead 时,系统会一直等待,直到收到数据或发生错误,然后系统会使用单独的线程执行指定的回调方法,并阻塞 EndRead,直到提供的 NetworkStream 读取数据或抛出异常。

于 2010-12-08T15:21:46.087 回答
0

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

一些伪

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

但是如果你的主线程没有其他事情要做并且你必须需要结果,你应该调用 Read。

于 2010-12-08T15:24:46.993 回答
0

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

于 2011-07-28T21:10:40.710 回答