2016-04-22 118 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