不断从NetworkStream async中读取

时间:2014-11-25 12:55:33

标签: c# .net asynchronous network-programming async-await

我是一位非常新的.NET开发人员,我目前正在阅读async / await。我需要研究用于测试设备的框架,这些设备通过使用TCP远程访问服务器以及从/向这些服务器读取/写入数据来控制。这将用于单元测试。

没有应用层协议,服务器可能会根据外部事件发送数据。因此,我必须能够连续捕获来自服务器的任何数据并将其写入缓冲区,缓冲区可以从不同的上下文中读取。

我的想法与以下代码段相似:

// ...
private MemoryStream m_dataBuffer;
private NetworkStream m_stream;
// ...

public async void Listen()
{
  while (Connected)
  {
    try
    {
      int bytesReadable = m_dataBuffer.Capacity - (int)m_dataBuffer.Position;

      // (...) resize m_dataBuffer if necessary (...)

      m_stream.ReadTimeout = Timeout;

      lock (m_dataBuffer)
      {
        int bytesRead = await m_stream.ReadAsync(m_dataBuffer.GetBuffer(), 
          (int)m_dataBuffer.Position, bytesReadable);
        m_stream.Position += bytesRead;
      }
    }
    catch (IOException ex)
    {
      // handle read timeout.
    }
    catch (Exception)
    {
      throw new TerminalException("ReadWhileConnectedAsync() exception");
    }
  }
}

这似乎有以下缺点:

  1. 如果呼叫并等待Listen功能,呼叫者将挂起,即使呼叫者必须能够继续(因为只要连接打开就应该读取网络流)。
  2. 如果声明它async void而不等待它,则应用程序会在任务中发生异常时崩溃。
  3. 如果声明async Task并且没有等待它,我会假设同样的情况发生(加上我收到警告)?
  4. 随后出现以下问题:

    • 如果我不等待它,我可以捕获Listen中引发的异常吗?
    • 是否有更好的方法可以使用async / await不断从网络流中读取数据?
    • 尝试使用async / await继续从网络流中读取,或者是一个更好的选择是不是真的很明智?

2 个答案:

答案 0 :(得分:1)

async void至少应为async Task并且丢弃返回值。这使得该方法符合理智的标准,并将责任推送给调用者,调用者可以更好地做出有关等待和错误处理的决策。

但你不必丢弃返回值。您可以附加日志记录延续:

async Task Log(Task t) {
 try { await t; }
 catch ...
}

并像这样使用它:

Log(Listen());

丢弃Log返回的任务(或等待它,如果你想在逻辑上等待)。

或者,只需将Listen中的所有内容包装在try-catch中即可。这似乎已经是这种情况了。

  

如果我不等待它,我可以捕获Listen中抛出的异常吗?

您可以使用任何附加延续或同步等待的方式查找异常(后者不是您的策略)。

  

是否有更好的方法可以使用async / await持续读取网络流?

不,这是它应该完成的方式。在任何给定时间应该有一个读取IO未完成。 (或者在短时间内为零。)

  

尝试使用async / await连续读取网络流是否真的是理智的,或者是一个更好的选择?

两者都能正常工作。 There is a trade-off to be made.同步代码可以更简单,更容易调试,甚至更少的CPU密集。异步代码保存在线程堆栈内存和上下文切换中。在UI应用中,await具有显着优势。

答案 1 :(得分:0)

我会做这样的事情:

const int MaxBufferSize = ... ;
Queue<byte> m_buffer = new Queue<byte>(MaxBufferSize);
NetworkStream m_stream = ... ;

...

// this will create a thread that reads bytes from 
// the network stream and writes them into the buffer 
Task.Run(() => ReadNetworkStream());


private static void ReadNetworkStream()
{
    while (true)
    {
        var next = m_stream.ReadByte();
        if (next < 0) break; // no more data

        while (m_buffer.Count >= maxBufferSize)
            m_buffer.Dequeue(); // drop front

        m_buffer.Enqueue((byte)next);
    }
}