【问题标题】:async await usage with TcpClient异步等待与 TcpClient 的使用
【发布时间】:2014-07-29 11:36:12
【问题描述】:

我最近开始使用新的 C#5.0“async”和“await”关键字。我以为我得到了转折,但意识到一件事让我怀疑。这是我从远程 TcpClient 异步接收数据的方式。一旦我接受连接,我就会调用这个函数:

static async void ReadAsync(TcpClient client)
{
    NetworkStream ns = client.GetStream();
    MemoryStream ms = new MemoryStream();
    byte[] buffer = new byte[1024];

    while(client.Connected)
    {
        int bytesRead = await ns.ReadAsync(buffer, 0, buffer.Length);
        ms.Write(buffer, 0, bytesRead);
        if (!ns.DataAvailable)
        {
            HandleMessage(ms.ToArray());
            ms.Seek(0, SeekOrigin.Begin);
        }
    }
}

接收到数据后,循环继续循环,不读取任何内容。我在循环中使用 Console.WriteLine 对此进行了测试。我是否正确使用它?我觉得我不应该使用 while 循环...

【问题讨论】:

  • 因此您已将数据写入MemoryStream,您在同一个MemoryStream 上一遍又一遍地调用HandleMessage,直到有更多数据可用。你预计会发生什么?
  • 你为什么在这里查看DataAvailable?这与消息等无关。其主要用途是决定是读取同步还是异步。您也不会清除后台缓冲区 - 您倒带,但您不会清除
  • 我觉得我最近一直在发这个帖子,但是 只使用async void 作为事件处理程序。不返回任何内容的异步方法应该是 async Task。 有很多原因,其中最重要的可能是未处理的异常会导致非常糟糕的意外行为。
  • @Marc 你确定DataAvaliable 吗?根据 MSDN:Use the DataAvailable property to determine if data is ready to be read. If DataAvailable is true, a call to Read returns immediately.
  • @Yuval 不,我说 调用者 可以使用它来决定是读取同步还是异步。如果它不为零,我们可以合理地使用同步读取并期望立即返回。如果它为零,我们应该切换到异步。它没有很多其他用途,而且大多数时候我看到人们触摸DataAvailable,他们不恰当地使用它(试图检测消息/帧的结尾 - 这是不是是什么意思)

标签: c# asynchronous tcp


【解决方案1】:

TcpClient.Connected 值不会立即更新。根据 MSDN:

true 如果客户端套接字在最近操作时连接到远程资源;否则,false。

因此将TcpClient.Connected 作为while 循环条件并不是一个好的选择。

TcpClient.DataAvailable用于同步操作,不用于异步。

将您的代码更新为:

static async void ReadAsync(TcpClient client)
{
    NetworkStream ns = client.GetStream();
    MemoryStream ms = new MemoryStream();
    byte[] buffer = new byte[1024];

    while(true) {
        int bytesRead = await ns.ReadAsync(buffer, 0, buffer.Length);
        if (bytesRead <= 0)
            break;
        ms.Write(buffer, 0, bytesRead);
        HandleMessage(ms.ToArray());
        ms.Seek(0, SeekOrigin.Begin);
    }
}

当ReadAsync返回0时,表示TCP连接关闭或关闭。在 MSDN 中:

TResult 参数的值包含读入缓冲区的总字节数。如果当前可用的字节数小于请求的数量,则结果值可以小于请求的字节数,或者如果已到达流的末尾,则结果值可以为 0(零)。

【讨论】:

    【解决方案2】:

    我最终使用了以长度为前缀的消息,为每个数据包添加了 4 个字节的前缀,表示即将到来的数据的长度。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-09-19
      • 2014-02-21
      • 1970-01-01
      • 1970-01-01
      • 2015-09-22
      • 2013-02-15
      相关资源
      最近更新 更多