Payload aufgeteilt auf zwei TCP-Pakete bei Verwendung von Boost ASIO, wenn es in die MTU passt

8

Ich habe ein Problem mit einem boost :: asio :: ip :: tcp :: iostream. Ich versuche ungefähr 20 rohe Bytes zu senden. Das Problem besteht darin, dass diese 20-Byte-Nutzlast in zwei TCP-Pakete mit 1 Byte und dann 19 Bytes aufgeteilt wird. Einfaches Problem, warum es passiert Ich habe keine Ahnung. Ich schreibe dies für ein veraltetes Binärprotokoll, das sehr viel verlangt, dass die Nutzlast in ein einzelnes TCP-Paket passt (Stöhnen).

Das Einfügen der ganzen Quelle aus meinem Programm wäre lang und übermäßig komplex, ich habe hier das Funktionsproblem nur innerhalb von 2 Funktionen veröffentlicht (getestet, reproduziert das Problem);

%Vor%

Um das merkwürdige Problem hinzuzufügen, wenn ich die Zeichenfolge "Hallo Welt." sende, geht es in einem Paket. Wenn ich 0x1, 0x2, 0x3 (die rohen Bytewerte) sende, bekomme ich in Paket 1 0x1, dann den Rest der Daten im nächsten TCP-Paket. Ich benutze wireshark, um die Pakete zu betrachten, es gibt nur einen Schalter zwischen dem Dev-Rechner und 192.168.1.1.

    
xconspirisist 27.07.2011, 15:24
quelle

5 Antworten

9

Ihr Code:

%Vor%

Führt 3 Aufrufe von operator<< function aus.

Wegen des Nagle-Algorithmus von TCP sendet der TCP-Stack die verfügbaren Daten ((char)0x1) sofort nach / während des ersten Aufrufs operator<< . Also werden die restlichen Daten (0x2 und 0x3) zum nächsten Paket gehen.

Lösung zum Vermeiden von 1 Byte TCP-Segmenten: Rufen Sie Sendefunktionen mit größeren Datenmengen an.

    
SKi 09.08.2011, 10:46
quelle
10

Mach dir keine Sorgen, du bist von der einzigen, die dieses Problem hat. Es gibt definitiv eine Lösung. Tatsächlich haben Sie ZWEI Probleme mit Ihrem alten Protokoll und nicht nur eins.

Ihr altes veraltetes Protokoll benötigt eine "Anwendungsnachricht", die in "ein und nur ein TCP-Paket" passt (weil es ein TCP-stream-orientiertes Protokoll fälschlicherweise als paketorientiertes Protokoll verwendet). Also müssen wir sicherstellen, dass:

  1. keine "Anwendungsnachricht" wird auf mehrere TCP-Pakete aufgeteilt (das Problem, das Sie sehen)
  2. Kein TCP-Paket enthält mehr als eine "Anwendungsnachricht" (Sie sehen dies nicht, aber es kann definitiv passieren)

Die Lösung:

Problem 1

Sie müssen Ihren Socket mit all Ihren "Nachricht" -Daten gleichzeitig füttern. Dies geschieht derzeit nicht, da, wie andere Leute es beschrieben haben, die Boost-Stream-API, die Sie verwenden, in getrennten Aufrufen Daten in den Socket eingibt, wenn Sie aufeinanderfolgende "& lt; & lt;" und der zugrunde liegende TCP / IP-Stack Ihres Betriebssystems puffert nicht genug (und mit Gründen, für eine bessere Leistung)

Mehrere Lösungen:

  • Sie übergeben einen Zeichenpuffer anstelle von separaten Zeichen, sodass Sie nur einen Aufruf an & lt; & lt;
  • senden
  • Sie vergessen boost, öffnen Sie einen OS-Socket und füttern Sie es in einem Aufruf zu senden () (in Windows, suchen Sie nach der "Winsock2" API, oder suchen Sie nach "sys / socket.h" auf Unix / cygwin)

Problem 2

Sie MÜSSEN die Option TCP_NODELAY auf Ihrem Socket aktivieren . Diese Option wurde speziell für solche Legacy-Protokollfälle entwickelt. Es stellt sicher, dass der OS-TCP / IP-Stack Ihre Daten "ohne Verzögerung" sendet und nicht zusammen mit einer anderen Anwendungsnachricht puffert, die Sie später senden können.

  • Wenn Sie bei Boost bleiben, suchen Sie nach der Option TCP_NODELAY, die sich im Dokument
  • befindet
  • Wenn Sie OS-Sockets verwenden, müssen Sie die Funktion setsockopt () auf Ihrem Socket verwenden.

Fazit

Wenn Sie diese beiden Probleme lösen, sollten Sie in Ordnung sein!

Die OS-Socket-API, entweder unter Windows oder Linux, ist ein wenig schwierig zu benutzen, aber Sie erhalten die volle Kontrolle über ihr Verhalten. Unix-Beispiel

    
Offirmo 09.08.2011 12:56
quelle
1

Ich bin mir nicht sicher, wer eine solche Anforderung hätte auferlegen müssen, dass eine gesamte Nutzlast innerhalb eines TCP-Pakets liegen muss. TCP ist von Natur aus ein gestreamtes Protokoll und viele der Details in der Anzahl der gesendeten Pakete und der Nutzlastgröße usw. sind der TCP-Stapelimplementierung des Betriebssystems überlassen.

Ich würde überprüfen, ob dies eine tatsächliche Einschränkung Ihres Protokolls ist oder nicht.

    
feathj 27.07.2011 17:31
quelle
1

Ich stimme der Antwort von User1 zu. Wahrscheinlich rufen Sie operator << mehrmals auf; Beim ersten Aufruf sendet es sofort das erste Byte über das Netzwerk, dann kommt der Nagle-Algorithmus ins Spiel, daher werden die verbleibenden Daten in einem einzigen Paket gesendet.

Aber selbst wenn die Paketierung kein Problem war, ist die Tatsache, dass Sie häufig eine Socket-Sendefunktion für kleine Datenmengen aufrufen, ein großes Problem. Jede für einen Socket aufgerufene Funktion ruft eine schwere Kernel-Modus-Transaktion (Systemaufruf) auf, wobei send für jedes Byte aufgerufen wird, ist einfach verrückt!

Sie sollten Ihre Nachricht zuerst im Speicher formatieren und dann senden. Für Ihr Design würde ich vorschlagen, eine Art Cache-Stream zu erstellen, der die Daten in seinem internen Puffer sammelt und sofort an den zugrunde liegenden Stream sendet.

    
valdo 09.08.2011 10:59
quelle
0

Es ist falsch, an Daten zu denken, die über einen TCP-Socket als Pakete gesendet werden. Es ist ein Stream von Bytes, wie Sie die Daten rahmenspezifisch gestalten.

  

Irgendwelche Vorschläge?

Ich schlage vor, Sie implementieren ein Protokoll, so dass der Empfänger weiß, wie viele Bytes zu erwarten sind. Ein beliebter Weg, dies zu erreichen, besteht darin, einen Header fester Größe zu senden, der die Anzahl der Bytes für die Nutzlast angibt.

    
Sam Miller 27.07.2011 16:25
quelle

Tags und Links