使用TcpListener和TcpClient的异步函数问题,函数不等待等待关键字

时间:2016-04-22 05:57:44

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

我是异步套接字编程的新手,我的异步函数出现了问题。

我正在尝试创建一个使用Windows Forms作为客户端的聊天程序,以及一个用于服务器的控制台应用程序。

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

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;
}

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

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

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

导致这些错误的原因是什么?我正在以正确的方式解决这个问题吗?

2 个答案:

答案 0 :(得分:2)

您的两个问题都与异步函数无关,实际上这两个问题都是由于同样的问题:

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

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

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

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

另请注意,您使用大缓冲区(256字节),而客户端\服务器发送短消息(例如&#34;客户端收到:xxx&#34;),这意味着大部分缓冲区为空,当您将其转换为字符串通过Encoding.ASCII.GetString - 你最后得到了很多空格(&#34;客户收到:xxx ......&#34;)。

答案 1 :(得分:1)

async / await看起来不像是TCP流的问题。

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

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

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