客户端;网络流;读取异步; C#

TcpClient; NetworkStream; ReadAsync; C#

请原谅我对任务和异步的了解不足。

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

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 将始终 return 0 字节。

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

谢谢,

不,TCP 不是这样工作的。

NetworkStream 被视为处于 "end of stream" 状态,当另一方已启动(可能 one-way)关闭时。那时 ReadAsync(或 Read,就此而言)returns 为零 - 在任何其他情况下都不会。

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

This method reads data into the buffer parameter and returns the number of bytes successfully read. If no data is available for reading, the Read method returns 0. The Read operation reads as much data as is available, up to the number of bytes specified by the size parameter. If the remote host shuts down the connection, and all available data has been received, the Read method completes immediately and return zero bytes.

请注意最后一句话,它告诉您 NetworkStream 是 "end of stream" 的实际含义。这就是 TCP 连接关闭的方式。

您对此的回应通常也应该是从另一端关闭连接 - return 从您的辅助方法中清除套接字。在任何情况下,都不要再重复 while (true) - 你只会得到一个无限循环,它会吃掉你的 CPU.

的 100%

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