4

请原谅我缺乏关于任务和异步的知识。

使用 TcpClient 类,我正在创建与可用服务器的连接:

void async RunClientAsync()
{
    TcpClient client = new TcpClient();
    try
    {
        await client.ConnectAsync(IPAddress.Parse("1.1.1.1"), 8889);
        Task.Start(() => ReadClientAsync(client));
    }
    catch (Exception ex)
    {
        HandleException(ex);
    }
}

// -----

void async ReadClientAsync(TcpClient client)
{
    byte[] bf = new byte[2048];
    try
    {
        while(true)
        {
            int br = await client.NetworkStream().ReadAsync();
            if (br > 0) 
            {
                HandleInboundData(bf, br);
            }
        }
    }
    catch (Exception ex)
    {
        HandleException(ex);
    }
}

辅助方法 HandleException(Exception ex) 和 HandleInboundData(byte[] buffer, int length) 将执行假定的任务。

与服务器的连接将是永久的,并且从服务器接收的数据的长度和频率将未知,其想法是抛出一个任务,仅在数据可用时接收和处理入站数据。

ReadClientAsync(TcpClient client) 明显失败,因为如果没有可用数据,ReadAsync 将始终返回 0 字节。

我应该如何使用 async / task 编写 ReadClientAsync 以防止繁忙循环的情况?我以前在这些情况下使用过 BeginRead / EndRead,效果很好。在这种特殊情况下,这会是解决方案吗?

谢谢,

4

1 回答 1

6

不,这不是 TCP 的工作方式。

NetworkStream当另一端已启动(可能是单向)关闭时,被认为处于“流结束”状态。那是ReadAsync(或Read,就此而言)返回零的时候 - 在任何其他情况下都不会。

MSDN 文档很容易被误解——主要是因为您正在查看错误的文档。NetworkStream不会覆盖ReadAsync(没有理由这样做),因此您实际上是在查看通用Stream.ReadAsync. 相比之下,文档NetworkStream.Read说:

该方法将数据读入缓冲区参数,并返回成功读取的字节数。如果没有数据可供读取,Read 方法返回 0。Read 操作读取可用数据,最多为 size 参数指定的字节数。如果远程主机关闭连接,并且已接收到所有可用数据,则 Read 方法将立即完成并返回零字节。

请注意最后一句话,它告诉您 aNetworkStream成为“流结束”的实际含义。这就是关闭 TCP 连接的方式。

您对此的回应通常也应该是从另一端关闭连接 -return从您的辅助方法中退出并清理套接字。在任何情况下,不要再重复一遍while (true)——你只会得到一个无限循环,它会占用你 100% 的 CPU。

如果您想了解如何使用 处理 C# 异步套接字await,请查看我在https://github.com/Luaancz/Networking/tree/master/Networking%20Part%202上的示例。请注意免责声明 - 这绝不是生产就绪的。但它确实解决了人们在实现 TCP 通信时常犯的一些错误。

于 2016-01-08T21:10:46.933 回答