2016-04-22 114 views
2

我是新来的异步套接字编程,我有我的异步函数的问题。TcpListener和TcpClient的异步函数问题,函数没有等待关键字

我正在尝试创建一个使用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; 
} 

的第一个问题,我有是当我运行客户端程序并单击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

你的问题,这两个不相关的异步函数,而实际上这两个问题都是因为同样的问题:

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

如果数据不尚未可从流中读取 - 无论你的ReceiveMessage和HandleConnections函数完全跳过阅读流。你应该做的,而不是(你的情况)是:

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

然后第一读取(或ReadAsync)将等到第一个数据块到达时,只有经过第一个块将检查更多的数据已经可用。

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

+0

哇,我没有想到像改变我的while循环一样简单的事情可以解决这个问题。非常感谢。我现在觉得有点愚蠢,因为我被这个问题困住了多久。 – PhantomWhiskers

+0

@PhantomWhiskers - 一个单独的问题,它即将咬你 - 你真的*需要注意'ReadAsync'返回的*值。它会告诉你*已收到多少个字节。你的很多代码似乎都假定你会收到与你所要求的字节数完全一样多的字节,这并不能保证。请记住,TCP的抽象是一个字节流,而不是*消息*。如果你想发送和接收消息,那么你需要在TCP上实现它,或者转移到更高层次的抽象。 –

1

它看起来不像async/await这样的问题,就像TCP流的问题一样。

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

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

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

+0

我改变了我的while循环做while循环就像上面的回答建议的那样,它解决了我的问题。感谢您的答复。 – PhantomWhiskers