不断从 NetworkStream 异步读取
本文关键字:异步 读取 NetworkStream | 更新日期: 2023-09-27 18:31:48
我是一个很新的人。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");
}
}
}
这似乎有以下缺点:
- 如果调用并等待
Listen
函数,则调用方将挂起,即使调用方必须能够继续(因为只要连接打开,就应读取网络流)。 - 如果将其声明为
async void
而不是等待它,则当任务中发生异常时,应用程序将崩溃。 - 如果将其声明为
async Task
并且不等待它,我假设也会发生同样的情况(加上我收到警告)?
以下问题随之而来:
- 如果我不等待,是否可以捕获
Listen
中抛出的异常? - 有没有更好的方法来使用
async
/await
不断从网络流中读取? - 尝试使用
async
/await
连续读取网络流实际上是理智的,还是线程是更好的选择?
async void
至少应该async Task
丢弃返回值。这使得该方法符合理智的标准,并将责任推给调用者,调用者更有能力做出有关等待和错误处理的决策。
但您不必丢弃返回值。您可以附加日志记录延续:
async Task Log(Task t) {
try { await t; }
catch ...
}
并像这样使用它:
Log(Listen());
扔掉Log
返回的任务(或者,如果您希望在逻辑上等待它),请等待它)。
或者,简单地将所有内容都包装在Listen
中。情况似乎已经如此。
如果我不等待,我可以捕获 Listen 中抛出的异常吗?
您可以使用任何附加延续或同步等待的方式(后者不是您的策略)来查找异常。
有没有更好的方法使用 async/await 不断从网络流中读取?
不,这是应该做的方式。在任何给定时间都应该有一个未完成的读取 IO。(或在短时间内为零。
尝试使用 async/await 连续读取网络流实际上是理智的,还是线程是更好的选择?
两者都将正常工作。需要做出权衡。同步代码可以更简单、更易于调试,甚至更少占用 CPU。保存在线程堆栈内存和上下文切换上的异步代码。在 UI 应用中,await
具有显著的优势。
我会做这样的事情:
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);
}
}