【问题标题】:Problems with asynchronous functions with TcpListener and TcpClient, function not waiting on await keywordTcpListener 和 TcpClient 的异步函数问题,函数不等待 await 关键字
【发布时间】:2016-08-15 14:56:07
【问题描述】:

我是异步套接字编程的新手,但我的异步函数有问题。

我正在尝试为客户端创建一个使用 Windows 窗体的聊天程序,并为服务器创建一个控制台应用程序。

这是处理我的服务器上的连接的代码:

public async void StartServer()
{
    TcpListener listener = new TcpListener(_ip, _port);
    listener.Start();
    Console.WriteLine("Server is running on IP: {0} Port: {1}", _ip.ToString(), _port);
    while (true)
    {
        try
        {
            TcpClient client = await listener.AcceptTcpClientAsync();
            HandleConnections(client);
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
    }
}
private async void HandleConnections(TcpClient client)
{
    NetworkStream stream = client.GetStream();
    byte[] buffer = new byte[256];
    string message = null;
    int x;
    while(stream.DataAvailable)
    {
        x = await stream.ReadAsync(buffer, 0, buffer.Length);
        message += Encoding.ASCII.GetString(buffer);
    }
    message = message.Replace('\0', ' ');
    message = message.Trim();
    Console.WriteLine("Message Recieved: " + message);
    byte[] bytes = Encoding.ASCII.GetBytes(message);
    await stream.WriteAsync(bytes, 0, bytes.Length);
    stream.Close();
}

这是客户端程序连接到服务器的代码:

private async void ConnectButton_Click(object sender, EventArgs e)
{
    IPAddress address = IPAddress.Parse(IPInput.Text);
    client = new TcpClient();
    await client.ConnectAsync(address, 12345);
    NetworkStream stream = client.GetStream();
    string message = UsernameInput.Text + " Connected!";
    Task<int> sendTask = SendMessage(stream, message);
    int sendComp = await sendTask;
    Task<string> recieveTask = RecieveMessage(stream);
    string recieved = await recieveTask;
    stream.Close();
    ChatText.AppendText(recieved);
}
private async Task<int> SendMessage(NetworkStream stream, string message)
{
    byte[] bytes = Encoding.ASCII.GetBytes(message + "\r\n");
    await stream.WriteAsync(bytes, 0, bytes.Length);
    return 1;
}
private async Task<string> RecieveMessage(NetworkStream stream)
{
    byte[] buffer = new byte[256];
    string message = null;
    int x;
    while (stream.DataAvailable)
    {
        x = await stream.ReadAsync(buffer, 0, buffer.Length);
        message += Encoding.ASCII.GetString(buffer);
    }
    return message;
}

我遇到的第一个问题是当我运行客户端程序并单击连接按钮时,消息被发送到输出Message Recieved: user Connected! 的服务器程序,但随后客户端程序在@ 行遇到空引用异常987654324@ 表示recieved 变量为空。似乎string recieved = await recieveTask; 行并没有等待任务完成执行,而是在没有为recieved 赋值的情况下跳转到下一行。如果我在private async Task&lt;string&gt; RecieveMessage(NetworkStream stream) 函数的顶部放置一个断点并单步执行它,那么recieved 变量将获得它的值并且代码将成功完成,但没有断点我会得到空引用异常。

我遇到的下一个问题是,如果我让服务器保持运行并再次打开客户端并尝试连接,服务器会在 message = message.Replace('\0', ' '); 行上获得一个空引用异常。第一次用客户端运行,服务端成功接收到消息,但第二次没有从流中获取任何数据,将变量留空,导致空引用异常。

如果我的代码是垃圾,我深表歉意,我已经阅读 MSDN 文档好几个小时了,无法提出解决方案,我觉得我这样做完全错了。所以我的问题如下:

是什么导致我遇到这些错误?我是否以正确的方式解决这个问题?

【问题讨论】:

    标签: c# .net async-await tcpclient tcplistener


    【解决方案1】:

    您的两个问题都与异步函数无关,实际上这两个问题都是因为同一个问题:

    while (stream.DataAvailable)
    {
        // read stream here
    }
    

    如果数据不能从流中读取 - 你的 ReceiveMessage 和 HandleConnections 函数都只是跳过读取流。你应该做的(在你的情况下)是:

    do
    {
        // read your stream here
    } while (stream.DataAvailable);
    

    然后第一个读取(或 ReadAsync)将等待直到第一个数据块到达,并且只有在第一个块之后才会检查是否有更多数据可用。

    还请注意,当客户端\服务器发送短消息(如“客户端收到:xxx”)时,您使用大缓冲区(256 字节),这意味着大部分缓冲区是空的,当您通过 Encoding.ASCII 将其转换为字符串时.GetString - 最后你会得到很多空白(“Client received: xxx ...”)。

    【讨论】:

    • 哇,我不认为像将我的 while 循环更改为 do while 这样简单的事情可以解决问题。太感谢了。我现在觉得有点愚蠢,因为我被这个问题困扰了多长时间。
    • @PhantomWhiskers - 一个单独的问题即将对你产生影响 - 你真的需要注意从@987654323返回的值@。它告诉你有多少字节已经被接收。您的许多代码似乎都假定您将收到与您要求的一样多的字节,但这并不能保证。请记住,TCP 的抽象是字节流,而不是消息。如果你想发送和接收消息,你可以在 TCP 上实现它,或者转移到更高层次的抽象。
    【解决方案2】:

    这看起来不像是 async/await 的问题,而是你的 TCP 流的问题。

    您似乎并没有真正在等待回复。 SendMessage 写入服务器,然后 RecieveMessage 期望响应已经在流中。

    如果第一次点击while 循环时stream.DataAvailable 为假,message 将保持null

    在尝试从流中读取数据之前,您需要一些方法来等待流中有数据。

    【讨论】:

    • 我将我的 while 循环更改为执行 while 循环,就像上面建议的答案一样,它解决了我的问题。感谢您的回复。
    猜你喜欢
    • 2011-04-07
    • 2020-12-23
    • 1970-01-01
    • 2019-03-02
    • 2023-03-13
    • 1970-01-01
    • 2019-11-28
    • 2020-01-22
    • 1970-01-01
    相关资源
    最近更新 更多