首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >NetworkStream.Read()和NetworkStream.BeginRead()之间的区别?

NetworkStream.Read()和NetworkStream.BeginRead()之间的区别?
EN

Stack Overflow用户
提问于 2010-12-08 14:48:23
回答 4查看 16.2K关注 0票数 8

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

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

MSDN上的文档表明,

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

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

在我的进一步研究中,我发现了BeginRead,它监视流,并在接收到数据时异步调用回调方法。我也尝试过使用这种方法寻找各种实现,但是,我无法确定什么时候使用BeginRead会比Read更有益。

在我看来,BeginRead只是拥有异步调用的优势,它不会阻塞当前线程。但是在我的应用程序中,我已经有了一个单独的线程来读取和处理流中的数据,所以这对我没有多大影响。

  • 请任何人帮助我理解BeginRead的等待和退出机制,以及它与Read有什么不同?
  • 实现所需功能的最佳方法是什么?
EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2010-12-08 15:12:10

我使用BeginRead,但是继续使用WaitHandle阻塞线程

代码语言:javascript
复制
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项目中复制和粘贴的完整流读取代码:

代码语言:javascript
复制
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.");
        }
    }
}
票数 12
EN

Stack Overflow用户

发布于 2010-12-08 15:21:46

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

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

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

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

票数 5
EN

Stack Overflow用户

发布于 2010-12-08 15:24:46

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

一些psudo

代码语言:javascript
复制
BeginRead()
//...do something in main object while result is fetching in another thread
var result = EndRead();

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

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4388771

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档