TcpClient; NetworkStream; ReadAsync; C#

本文关键字:ReadAsync NetworkStream TcpClient | 更新日期: 2023-09-27 18:29:30

请原谅我对任务和异步缺乏了解。

使用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客户端)是一个明显的失败,因为如果没有可用的数据,ReadAsync将始终返回0字节。

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

谢谢你,

TcpClient; NetworkStream; ReadAsync; C#

不,TCP不是这样工作的。

当另一侧启动(可能是单向)关闭时,NetworkStream被认为处于"流结束"状态。这时ReadAsync(或Read)返回零——在任何其他情况下都不会。

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

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

请注意最后一句,它告诉了NetworkStream成为"流的结尾"的实际含义。TCP连接就是这样关闭的。

对此的响应通常也应该是关闭另一端的连接-从helper方法中取出return并清理套接字。无论如何,不要再重复while (true)——你只会得到一个消耗100%CPU的无限循环。

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