Wie reagiert der Neuübertragungsmechanismus, wenn ein TCP-Paket teilweise bestätigt wurde?

12

Wenn ein TCP-Client ein Paket mit einer Sequenznummer von 10000 bis 20000 an einen TCP-Server sendet. Der TCP antwortet mit einem ACK mit seq_ack 20001.

Wenn ich das TCP-Paket vom Client abfange und das Paket in 2 TCP-Segmente aufspalte, eines mit einer Sequenz von 10000 bis 15000 und das andere mit einer Sequenz von 15001 bis 20000. Und dann werden diese 2 TCP-Segmente an den TCP-Server gesendet. Angenommen, das zweite Segment ist im Pfad verloren. Der TCP-Server antwortet mit seq_ack 15001 auf eine ACK.

Da der TCP-Client nun ein integrales Paket mit der Sequenz 10000 bis 20000 sendet, aber aus Sicht des Clients eine ACK mit 15001 erhält, ist dies seltsam. Wie wird es reagieren? Theoretisch sollte der Client die Bytes von 15001 bis 20000 erneut übertragen, dh, der Client überträgt ab 15001 neue Pakete. Wie sieht es jedoch bei der Implementierung des TCP-Stacks mit der Theorie aus?

Ich denke, im TCP-Sendepuffer, wenn ein TCP-Segment gesendet wird, bleibt das Segment immer noch dort, bis die ACK. Wenn die ACK kommt, werden diese Bytes für das Segment aus dem Puffer gelöscht. Es gibt einen Zeiger im Sendepuffer, wenn ein ACK kommt, zeigt der Zeiger auf die Stelle, an der ack_seq entspricht. Die Bytes unterhalb von ack_seq werden gelöscht. Auf diese Weise muss nicht das gesamte Segment erneut übertragen werden?

misteryes
quelle

Antworten:

8

Dies wird als selektive Bestätigung bezeichnet und ist bereits in der in RFC 2018 definierten TCP-Spezifikation enthalten . Dies würde es dem Client ermöglichen, tatsächlich nur die Bytes 15001 bis 20000 erneut zu senden (da sie sich in verschiedenen Paketen / Segmenten befinden, wenn Sie sie wie gesagt aufgeteilt hätten), aber interessanter ist, dass sogar Bestätigungen außerhalb der Reihenfolge möglich sind.

Ab RFC 2018:

Beim Empfang einer ACK mit einer SACK-Option MUSS der Datensender die selektive Bestätigung zur späteren Bezugnahme aufzeichnen. Es wird angenommen, dass der Datensender über eine Neuübertragungswarteschlange verfügt, die die übertragenen, aber noch nicht bestätigten Segmente in der Reihenfolge der Folgenummern enthält.

Unterstützende SACKwird nicht durch die TCP - Spezifikation erforderlich. Wenn entweder der Client oder der Server keine selektive Bestätigung unterstützen würden, müssten tatsächlich alle Bytes 10000 bis 20000 erneut übertragen werden.

Ist es bei der Implementierung des TCP-Stacks dasselbe wie in der Theorie?

In der Regel SACK wird dies unterstützt, da die Leistung, Effizienz und Latenz erheblich sind - insbesondere in einem Netzwerk wie dem Internet.

Diese Annahmen sollten jedoch auch dann zutreffen, wenn Sie die Pakete wie angegeben manuell manipulieren. Wie pro RFC 793 , bei Minimum, wird das gesamte Datenfenster erneut übertragen werden, aber der Empfänger nicht wissen , dass die Recieved Daten zumindest ist gültig . Einzelheiten zur Implementierung finden Sie in Abschnitt 3.3 - Sequenznummern aus RFC 793.

Eine Übersicht über den gesamten Prozess mit und ohne Unterstützung für selektive Bestätigungen finden Sie in diesem Artikel (der einige sehr nützliche Diagramme enthält).

Durchbruch
quelle
Für mich ist das etwas seltsam, da TCP ein streambasiertes, byteorientiertes Protokoll ist. Warum sollte das gesamte Segment erneut übertragen werden? Es scheint mir, dass TCP ohne SAKC ein segmentorientiertes Stream-Protokoll ist, aber TCP mit Sack ist wirklich byteorientiert. Ich denke, der RFC geht nicht speziell darauf ein.
misteryes
Wie der TCP-Stack seinen Sendepuffer verwaltet, ist dasselbe wie das, was ich in der aktualisierten Frage geschrieben habe.
misteryes
@misteryes dieser Artikel beschreibt den Prozess (mit einigen tollen Diagrammen auch!).
Durchbruch
In dem von Ihnen empfohlenen Link scheint der Autor das Problem weiterhin segmentorientiert und nicht wirklich byteorientiert zu diskutieren. Ist es nicht
misteryes
1
Ich habe SACK gekannt, bevor ich diese Frage gestellt habe. Anfangs glaube ich nicht, dass SACK etwas mit dieser Frage zu tun hat. Wenn TCP meiner Meinung nach nicht byteorientiert, sondern segmentorientiert ist, sollte auch SACK dasselbe sein. Der Unterschied zwischen SACK-enabled und SACK-disabled besteht darin, dass TCP bei SACK eine Sequenzlücke in ack_seq zulässt. Aber ich dachte, das Sequenzloch entspricht einem Segment. während nach Ihrem Sprichwort das Loch die Hälfte / Teil eines Segments sein kann.
misteryes
3

Die Segmentgrößen können sich über die Lebensdauer einer Verbindung ändern. Glücklicherweise muss TCP nicht die Segmentgröße aufzeichnen, mit der einzelne Pakete zuvor gesendet wurden. Daher wird Folgendes ausgeführt:

  1. Wenn eine ACK eintrifft, stellen Sie den Zeiger entsprechend auf das erste unbestätigte Byte und verwerfen Sie alle nicht mehr benötigten Puffer.
  2. Wenn eine erneute Übertragung erforderlich ist (Fast Retransmit oder Timeout; NICHT unmittelbar nach dem Empfang der ersten ACK!), Wird die aktuell gültige Segmentgröße erneut gesendet, beginnend mit dem Zeiger bis zum ersten unbestätigten Byte.

Beide Operationen werden unabhängig von der Segmentgröße ausgeführt, in der diese Bytes ursprünglich gesendet wurden. Daher sollte die Theorie mit den meisten Implementierungen übereinstimmen.

Lassen Sie mich einige Hintergrundinformationen geben, um Folgendes zu erklären:

Verwendet TCP Bytes oder Segmente? Für die Anwendung macht TCP eine Byte-Stream-Schnittstelle verfügbar. Außerdem sind alle Headerfelder und internen Variablen in Bytes angegeben. Um Informationen zu übertragen, zerlegt TCP sie jedoch in Segmente, da das Senden von Bytes nacheinander ziemlich verschwenderisch wäre :-). Die Verwendung von Byte-Zählern überall hat den Vorteil, dass die Segmentgröße über die Lebensdauer der Verbindung nicht konstant bleiben muss:

  • Es werden Optionen eingeführt, z. B. das Huckepack-Tragen eines SACKs bei einer erneuten Übertragung (bei realen Implementierungen wird dies, wenn überhaupt, selten der Fall sein).
  • Die Pfad-MTU ändert sich, z. B. wechselt eine Verbindung entlang des Pfads zu einer niedrigeren MTU oder die Engpass-MTU-Verbindung wird ausgelöst. Dies geschieht, wenn Tunnel eingerichtet werden (VPN, PPPoE) oder das Routing-Protokoll eine andere MTU-Verbindung auswählt. Dies geschieht in IPv4 mit aktivierter Option "Nicht fragmentieren" (gilt für die meisten modernen TCPs). immer in TCPv6).

Übrigens: SACK ist hier nicht die Antwort, da der Empfänger SACK (normalerweise) nur verwendet, wenn er eine Lücke im Bytestrom erkennt (dh wenn ein Paket verloren gegangen ist, aber ein nachfolgendes Paket eingetroffen ist).

Marcel Waldvogel
quelle