2017-12-30 36 views
0

Mein Szenario: Ich habe einen Asynchron-TCP-Server, die Verbindungen akzeptiert, und es antwortet mit dieser Methode auf Anfragen:TCP-Client manchmal nur ein Teil der Nachricht erhält

 private void SendMessage(Client client, string message) 
    { 
     var buffer = Encoding.UTF8.GetBytes(message); 
     try 
     { 
      client.TCPClient.GetStream().Write(buffer, 0, buffer.Length); 
     } 
     catch (Exception ex) when (ex is InvalidOperationException || ex is System.IO.IOException) 
     { 
      RemoveClient(client); 
      Task exceptionHandler = Program.ExceptionHandler.Server(ex); 
     } 
    } 

Und ich habe eine Anwendungs-Client, der erhält Daten mit dieser Methode:

private string GetMessage() 
    { 
     StringBuilder returndata = new StringBuilder(); 
     NetworkStream clientStream = this.tcpClient.GetStream(); 
     var sr = new StreamReader(clientStream, Encoding.UTF8); 
     int value; 
     while (sr.Peek() >= 0 || clientStream.DataAvailable) 
     { 
      value = sr.Read(); 
      returndata.Append((char)value); 
     } 
     return returndata.ToString(); 
    } 

Meist I Objekte serialisiert mit JSON, senden und es hat bis jetzt gut funktioniert. Wenn ich den Client neu starte, ergreift es Daten vom Server, aber manchmal kommt nur ein Teil davon und die Anwendung stürzt mit ArgumentException bei JSON deserialize ab. Es ist seltsam, dass, wenn es passiert (zufällig) sr.Peek() = -1, aber clientStream.DataAvailable = true

Eine andere Information ist, dass immer, wenn es die readed Nachricht stürzt gleich ist.

Jeder konnte mir helfen, was dazu führen konnte, dass ich manchmal nur einen Teil der Nachricht nicht alle bekam?

+0

Ein Schreibvorgang entspricht nicht einem Lesevorgang. Siehe letztes Beispiel [hier] (https://msdn.microsoft.com/en-us/library/system.net.sockets.socket (v = vs.110) .aspx) für ein Beispiel. Wenn Sie in msdn suchen, gibt es weitere Beispiele. – CodingYoshi

+0

Related: [Unterbrochene TCP-Nachrichten] (https://stackoverflow.com/q/7257139). – dbc

Antwort

0

Die TCP-APIs bieten einen bidirektionalen Byte-Stream in beide Richtungen - keine Paket-API. Sie schreiben in den Stream an einem Socket und lesen am anderen Ende davon. Es gibt jedoch keine Garantie dafür, dass ein einzelner Schreibvorgang genau einen oder mehr Lesevorgänge verursacht. Dies liegt daran, dass die gesendeten Bytes möglicherweise über mehrere IP-Pakete verteilt werden und einige möglicherweise verspätet ankommen.

Wenn Sie Pakete oder Nachrichten auf Ihrer Anwendungsschicht haben möchten, müssen Sie Ihren eigenen Framing-Mechanismus einführen.

Typische Lösungen, dies zu tun, sind:

  • Präfix jede Nachricht mit ihrer Gesamtlänge, die in einem bekannten Format (zum Beispiel 4 Byte Big-Endian-Nummer) serialisiert wird. Dann kann die empfangende Seite auf die Länge warten und danach genau diese Anzahl von Bytes lesen und sie als eine Nachricht interpretieren.
  • Verwenden Sie einige Trennzeichen, um Nachrichten zu trennen. Z.B. im einfachsten Fall ein Zeilenumbruch. Dies hat jedoch den Nachteil, dass dieses Zeichen möglicherweise nicht in Ihrer Nachrichtennutzlast enthalten ist (muss maskiert sein) und dass der Empfänger danach suchen muss. Dies ist jedoch z.B. der Mechanismus, der in HTTP verwendet wird, um den Kopf- und den Körperteil zu trennen.
0

Sie können nicht darauf zählen, dass ein einzelner Empfang alle gesendeten Daten erhält. Sie müssen die Anzahl der empfangenen Bytes überprüfen und prüfen, ob es sich um eine vollständige Nachricht handelt.

+0

Ok, ich denke du hast Recht, aber wie kann ich das in dieser Situation lösen? Es wäre nett, wenn Sie einen Rat oder einen Link geben, alles :) – user8932376