使用TcpClient的异步等待用法

6

最近我开始使用新的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 循环...


你为什么在这里检查 DataAvailable?那与消息等没有任何关系。主要用途是决定是读取同步还是异步。你也从未清除后备缓冲区 - 你倒带,但你没有清除。 - Marc Gravell
3
最近我似乎经常发布这个内容,但请注意:只有在事件处理程序中使用async void。不返回任何内容的异步方法应该使用async Task。有许多原因,其中最重要的原因可能是未处理的异常可能会导致非常糟糕、意想不到的行为。 - Daniel Mann
@Marc,你确定DataAvaliable吗?根据MSDN:使用DataAvailable属性来确定数据是否准备好进行读取。如果DataAvailable为真,则对Read的调用将立即返回。 - Yuval Itzchakov
@DanielMann 谢谢你,我会修复我的代码! - Philippe Paré
1
@Yuval 不是的,我说的是调用者可以使用它来决定是读取同步还是异步。如果它非零,我们可以合理地使用同步读取并期望迅速返回。如果为零,则应切换到异步。它没有太多其他用途,大多数时候我看到人们触及DataAvailable时,他们都在不适当地使用它(尝试检测消息/帧的结尾 - 这不是它的含义)。 - Marc Gravell
显示剩余8条评论
2个回答

6

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(零)。


0

最终我采用了长度前缀消息,即在每个数据包之前加上四个字节来表示后续数据的长度。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接