2016-04-22 5 views
2

Ich bin neu in der asynchronen Socket-Programmierung, und ich habe Probleme mit meinen asynchronen Funktionen.Probleme mit asynchronen Funktionen mit TcpListener und TcpClient, Funktion wartet nicht auf erwarten Stichwort

Ich versuche, ein Chat-Programm zu erstellen, das Windows Forms für den Client und eine Konsolenanwendung für den Server verwendet. Hier

ist der Code für Verbindungen auf meinem Server Handhabung:

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

Und hier ist der Code für das Client-Programm eine Verbindung zum Server:

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

Das erste Problem, das ich habe, Wenn ich das Clientprogramm ausführe und auf den ConnectButton klicke, wird die Nachricht an das Serverprogramm gesendet, das Message Recieved: user Connected! ausgibt, aber dann findet das Clientprogramm eine Nullreferenzausnahme in der Zeile ChatText.AppendText(recieved);, die besagt, dass recieved Variable ist null. Es scheint, dass die Zeile string recieved = await recieveTask; nicht auf die Ausführung der Task wartet und in die nächste Zeile springt, ohne einen Wert recieved zuzuweisen. Wenn ich einen Haltepunkt an der Spitze der private async Task<string> RecieveMessage(NetworkStream stream)-Funktion setze und durch es gehe, dann erhält die recieved Variable ihren Wert und der Code wird erfolgreich abgeschlossen, aber ohne den Haltepunkt erhalte ich die Null-Referenz-Ausnahme.

Das nächste Problem, das ich habe, ist, wenn ich den Server laufen lassen und den Client erneut öffnen und versuchen, eine Verbindung herzustellen, erhält der Server eine Null-Referenz Ausnahme in der Zeile message = message.Replace('\0', ' ');. Beim ersten Mal, wenn ich mit dem Client arbeite, empfängt der Server die Nachricht erfolgreich, aber beim zweiten Mal erhält er keine Daten aus dem Stream und lässt die Variable leer, was zu einer Null-Referenz-Ausnahme führt.

Ich entschuldige mich, wenn mein Code Müll ist, habe ich die MSDN-Dokumentation für Stunden gelesen und bin nicht in der Lage, mit einer Lösung zu kommen, und ich fühle mich wie ich mache das völlig falsch. Also meine Fragen sind wie folgt:

Was verursacht diese Fehler, denen ich begegne? Und gehe ich dieses Problem richtig an?

Antwort

2

Ihre beiden Fragen sind nicht auf asynchrone Funktionen im Zusammenhang, und tatsächlich beide Fragen sind wegen des gleichen Problems:

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

Wenn Daten nicht noch zur Verfügung aus dem Stream lesen - Ihre beiden Die Funktionen ReceiveMessage und HandleConnections überspringen den Lesestream überhaupt. Was sollten Sie stattdessen tun (in Ihrem Fall) ist:

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

Dann zuerst lesen (oder ReadAsync) wartet, bis erster Datenblock ankommt, und erst nach dem ersten Chunk prüft, ob mehr Daten bereits zur Verfügung steht.

Beachten Sie auch, dass Sie großen Puffer (256 Bytes) während Client \ Server kurze Nachrichten senden (wie "Client empfangen: xxx"), was bedeutet, dass der Puffer größtenteils leer ist und wenn Sie es über Encoding in Zeichenfolge konvertieren. ASCII.GetString - am Ende erhalten Sie eine Menge Leerzeichen ("Client recommended: xxx ...").

+0

Wow, ich dachte nicht, dass etwas so einfach ist wie meine while-Schleife zu ändern, während das Problem behoben würde. Ich danke dir sehr. Ich fühle mich jetzt irgendwie blöd wegen der langen Zeit, in der ich mit diesem Thema beschäftigt war. – PhantomWhiskers

+0

@PhantomWhiskers - ein separates Problem, das dich beißen wird - du * musst * wirklich auf den Wert achten, der * von * ReadAsync zurückgegeben wird. Es zeigt Ihnen * wie viele * Bytes empfangen wurden. Ein Großteil Ihres Codes scheint davon auszugehen, dass Sie genau so viele Bytes erhalten haben, wie Sie angefordert haben, und das ist nicht garantiert. Bedenken Sie, dass die Abstraktion von TCP ein Stream von Bytes ist, keine * Nachrichten *. Wenn Sie Nachrichten senden und empfangen möchten, liegt es an Ihnen, dies über TCP zu implementieren oder zu einer Abstraktion höherer Ebene zu wechseln. –

1

Es scheint kein Problem mit async/await so viel wie ein Problem mit Ihren TCP-Streams.

Sie scheinen nicht auf eine Antwort zu warten. schreibt den Server, dann RecieveMessage erwartet eine Antwort bereits im Stream sein.

Wenn stream.DataAvailable falsch ist, wenn Sie die while Schleife zum ersten Mal treffen, wird messagenull bleiben.

Sie müssen warten, bis Daten im Stream vorhanden sind, bevor Sie versuchen, daraus zu lesen.

+0

Ich habe meine While-Loops geändert, um While-Loops wie die obige Antwort zu machen, und es hat mein Problem behoben. Danke für die Antwort. – PhantomWhiskers