2012-09-26 1 views
6

Ich lese gerade einen Artikel, der TCPClient.Read() sagt möglicherweise nicht alle gesendeten Bytes in einem lesen. Wie erklären Sie das?Wie berücksichtigen Sie, wenn TCP nicht alle Bytes in einem liest

Zum Beispiel kann der Server eine Zeichenfolge in den TCP-Stream schreiben. Der Client liest die Hälfte der Bytes der Zeichenfolge und liest dann die andere Hälfte in einem anderen Leseanruf.

Woher wissen Sie, wenn Sie die Byte-Arrays kombinieren müssen, die in beiden Aufrufen empfangen werden?

Antwort

16

woher wissen Sie, wenn Sie die Byte-Arrays kombinieren müssen, die in beiden Aufrufen empfangen werden?

Sie müssen dies auf Protokollebene entscheiden. Es gibt vier gängige Modelle:

  • Ende-in-Ende: Jede Seite kann nur eine einzige "Nachricht" pro Verbindung senden. Nach dem Senden der Nachricht schließen sie die Sendeseite des Sockets. Die empfangende Seite liest weiter, bis sie das Ende des Streams erreicht.
  • Längenpräfix: Geben Sie vor jeder Nachricht die Anzahl der Bytes in der Nachricht ein. Dies könnte in einem Format mit fester Länge (z. B. immer 4 Bytes) oder in einem komprimierten Format (z. B. 7 Bits von Größendaten pro Byte, oberes Bit, das für das letzte Byte von Größendaten eingestellt ist) sein. Dann gibt es die Nachricht selbst. Der empfangende Code liest die Größe und liest dann viele Bytes.
  • Chunking: Wie Längenpräfix, aber in kleineren Stücken. Jedem Chunk ist die Länge vorangestellt, mit einem letzten Chunk, der "Ende der Nachricht" anzeigt.
  • Ende-der-Nachricht-Signal: Lesen Sie weiter, bis Sie den Terminator für die Nachricht sehen. Dies kann ein Problem sein, wenn die Nachricht in der Lage sein soll, beliebige Daten einzuschließen, da Sie einen Escaping-Mechanismus benötigen, um die Terminator-Daten in der Nachricht darzustellen.

Zusätzlich seltener gibt es Protokolle, bei denen jede Nachricht immer eine bestimmte Größe ist - in diesem Fall müssen Sie nur gehen zu halten, bis Sie, dass viele Daten gelesen habe.

In all diesen Fällen müssen Sie im Grunde Schleifen, Lesen von Daten in eine Art Puffer, bis Sie genug davon haben, aber Sie bestimmen, dass. Sie sollten immer verwenden Sie den Rückgabewert von Read zu beachten, wie viele Bytes Sie tatsächlich lesen, und immer überprüfen Sie, ob es 0 ist, in diesem Fall haben Sie das Ende des Streams erreicht.

Beachten Sie, dass dies nicht nur Netzwerk-Streams betrifft - für etwas anderes als eine lokale MemoryStream (die immer so viele Daten lesen, wie Sie in einem Schritt fragen, wenn es überhaupt im Stream ist), sollten Sie gehe davon aus, dass Daten möglicherweise nur im Verlauf mehrerer Anrufe verfügbar werden.

+0

oder erstellen Sie Ihr eigenes Protokoll: D – EaterOfCode

+0

Oder Sie könnten ein Protokoll fester Länge haben, in dem Sie die Länge der Nachrichten im Voraus kennen. Ich kann mir jedoch kein sinnvolles Beispiel vorstellen :( – NPSF3000

+0

@EaterOfCorpses: Dies sind Optionen * in * einem Protokoll. Wie Sie das Ende einer Nachricht erkennen, ist nur ein Teil des Protokolls. –

-1

Sie sollten read() in einer Schleife aufrufen. Die Bedingung dieser Schleife würde prüfen, ob noch irgendwelche Daten zum Lesen verfügbar sind.

+3

Das ist nur gültig, wenn die Verbindung nach dem Senden der Nachricht von der Sendeseite beendet wird. Es funktioniert nicht, wenn Sie mehrere Nachrichten auf dieselbe Verbindung stellen möchten. –

+0

Oh. Wusste das nicht. Vielen Dank. – Paul

+1

Es gibt nur zwei Möglichkeiten, um zu prüfen, ob noch irgendwelche Daten zu lesen sind. Eine ist, nach EOS zu suchen, was ungültig ist, wie @JonSkeet oben bemerkt hat. Das andere ist 'available()', alias 'FIO_NREAD', das Ihnen einfach sagt, wieviele Bytes bereits im Socket-Empfangspuffer angekommen sind und ohne Blockierung gelesen werden können: es sagt Ihnen nicht, wieviel Daten übrig sind * im Strom, * weil es nicht weiß. Niemand kann es wissen. Der Absender könnte weiterhin Daten für immer senden. – EJP

-3

Das ist ein bisschen schwer zu beantworten, weil Sie nie wissen können, wann Daten ankommen, und deshalb verwende ich normalerweise einen Thread für den Empfang von Daten in meinem Chat-Programm. Aber Sie sollten in der Lage sein, etwas zu verwenden, ähnlich wie diese:

do{ 
    numberOfBytesRead = myNetworkStream.Read(myReadBuffer, 
               0, 
               myReadBuffer.Length); 

    myCompleteMessage.AppendFormat("{0}", 
     Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead)); 

    } 
    while(myNetworkStream.DataAvailable); 

Schauen Sie sich das source!

+4

Definitiv * definitiv * nicht. Das wird aufhören, sobald die Daten nicht sofort verfügbar sind. Wenn also ein Paket verzögert wird, erhalten Sie die Hälfte der Daten. (Es ist auch nur ASCII, was heutzutage fast nie eine gute Idee ist.) Selbst wenn "DataAvailable" blockiert wäre, wären Sie dann auf eine einzelne Nachricht in einer Verbindung beschränkt. Das ist * schlechter * Code. –

+1

Das sind gute Informationen, auch wenn es falsch ist. Es ist ein "wie es nicht geht" –

Verwandte Themen