Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# NetworkStream.Read()和NetworkStream.BeginRead()之间的区别?_C#_.net_Multithreading_Networkstream - Fatal编程技术网

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)行之后直接调用该代码;以消除重复。