2.3.8 Behandlung von Paketverlusten

Um den Verlust von Paketen beim Transport im Netz bemerken zu können, startet TCP für jedes ausgesendete Segment einen Timer, dessen Wert angibt, wie lange der Sender noch auf eine Empfangsbestätigung wartet. Trifft die Quittung der Gegenseite nicht bis zum Ablauf des Timers ein, dann kommt es zum Timeout.

In diesem Falle nimmt TCP an, daß entweder das Paket oder dessen Bestätigung verlorengegangen ist, und reagiert mit einer Retransmission, d.h., das Segment wird noch einmal gesendet. Eine identische Wiederholung von Paketen ist dabei nicht zwingend. Um möglicherweise die Performance zu erhöhen, darf TCP eine sog. Repacketization vornehmen, d.h., es ist zulässig, mehrere Segmente zu einem größeren Paket zusammenzufassen, sofern dadurch die MSS nicht überschritten wird.

Die Behandlung von Timeouts und Sendewiederholungen gehört zu den wichtigsten und auch komplexesten Aspekten des TCP. An den verwendeten Algorithmen erkennt man ganz deutlich, daß dieses Protokoll für den Einsatz im weltweiten Internet entworfen wurde. Bedingt durch die Struktur dieses virtuellen Netzes läßt sich nicht a priori sagen, wie schnell eine Bestätigung beim Sender eintreffen wird. TCP muß daher in der Lage sein, sich sowohl an die großen zeitlichen Unterschiede beim Erreichen verschiedener Ziele als auch an die z.T. beträchtlichen Zeitschwankungen bei der Kommunikation mit ein und demselben Ziel anzupassen.

Pro Verbindung existieren vier Timer:

  1. Ein Retransmission-Timer wird gestartet, wenn der Sender auf eine Bestätigung des Empfängers wartet. Sobald er abläuft, kommt es zur Sendewiederholung.

  2. Der Persist-Timer sorgt dafür, daß auch dann, wenn die Gegenseite ihr Empfangsfenster geschlossen hat, weiterhin in bestimmten Abständen Meldungen ausgetauscht werden, die die aktuelle Fenstergröße anzeigen.

  3. Mit dem Keepalive-Timer kann TCP selbst bei einer zur Zeit ungenutzten Verbindung einen Crash des Partners erkennen.

  4. Der 2MSL-Timer mißt die Zeit, wie lange eine Verbindung schon im Zustand TIME-WAIT steht. Darauf sind wir bereits bei der Beschreibung des TCP-Automaten eingegangen.

TCP verwendet zur Retransmission einen adaptiven Algorithmus, d.h, es mißt permanent die Performance einer Verbindung und leitet daraus geeignete Timeout-Werte ab. Wenn sich der Durchsatz verändert, werden die Timeouts entsprechend angepaßt.

Um die für den adaptiven Algorithmus benötigten Daten zu sammeln, mißt TCP, wie lange es dauert, bis für ein ausgesendetes Byte mit einer bestimmten Sequenznummer eine Bestätigung eintrifft, die diese Sequenznummer einschließt. Die so ermittelte Zeitspanne wird als Round-Trip Time (RTT) bezeichnet. Man betrachtet die einzelnen Bytes statt der Segmente, da normalerweise keine Eins-zu-Eins-Beziehung zwischen Segmenten und Quittungen besteht.

Aus allen bisher gemessenen RTTs berechnet TCP eine mittlere RTT. Dieser Wert wird nach jeder neuen Messung aktualisiert. Es gibt verschiedene Methoden, einen solchen Durchschnitt zu berechnen. In der Original-Spezifikation von TCP ist die Bildung eines als Smoothed Round-Trip Time (SRTT) bezeichneten gewichteten Mittels nach folgender Formel beschrieben:

neue_SRTT = (ALPHA * alte_SRTT) + ((1-ALPHA) * zuletzt_gemessene_RTT)

Der Glättungsfaktor ALPHA liegt zwischen 0 und 1. Er bestimmt, welchen Einfluß die jeweils zuletzt gemessene RTT auf die SRTT ausübt. Ein relativ großer Faktor macht den Durchschnitt ziemlich unempfindlich gegenüber kurzzeitigen Veränderungen. Bei kleinem ALPHA reagiert die SRTT dagegen sehr schnell auf veränderte Verzögerungen im Netz.

Der Standard schlägt weiterhin vor, den Retransmission Timeout (RTO), d.h. den Initialwert des Retransmission-Timers, wie folgt zu ermitteln:

RTO = BETA * SRTT

BETA ist ein konstanter Wichtungsfaktor, der die Varianz der Verzögerungszeiten berücksichtigen soll. Er wurde früher meist mit 2 angenommen. Werte nahe 1 haben zur Folge, daß Paketverluste schnell entdeckt werden. Damit steigt der Durchsatz, da TCP nicht unnötig lange wartet, bis es eine Retransmission auslöst.

Wenn man allerdings BETA zu klein wählt, kann auch das Gegenteil eintreten, d.h. eine unnötige Sendewiederholung und damit eine Verschwendung von Bandbreite bzw. die weitere Erhöhung einer ohnehin schon hohen Last.

Untersuchungen haben ergeben, daß die bisherigen Formeln zur Ermittlung von SRTT und RTO nicht geeignet sind, sobald die gemessenen RTT-Werte stark streuen. Deshalb fordert RFC 1122, zur Bestimmung des RTO den nach seinem Autor Van Jacobson benannten und im Anhang A seines Papers Congestion Avoidance and Control angegebenen Jacobson's RTO Estimation Algorithm zu verwenden, bei dem in die Schätzung der SRTT die Streuung der RTTs einfließt. Damit lassen sich letztlich eine bessere Anpassung an stark schwankende Verzögerungszeiten sowie ein deutlich höherer Durchsatz erzielen.

Jacobson nutzt als Streuungsmaß nicht die relativ aufwendig zu berechnende Standardabweichung, sondern eine leicht zu bestimmende, in den meisten Fällen recht gute Näherung: die mittlere Abweichung. Sein Algorithmus läßt sich in einer an der Programmiersprache PASCAL orientierten Notation so angeben:

ERR := RTT - SRTT

SRTT := SRTT + g * ERR

DEV := DEV + h * (abs(ERR) - DEV)

RTO := SRTT + n * DEV

Der Operator := symbolisiert die Zuweisung des Wertes des rechts von ihm stehenden Ausdrucks an die links von ihm stehende Variable. Die Variablen werden genau wie oben wieder bei jeder neuen RTT-Messung aktualisiert. Wenn dabei ein Operand auf beiden Seiten des Zuweisungs-Operators auftritt, dann fließt der rechts stehende alte Wert in die Berechnung des links stehenden neuen Wertes ein, so wie das bei prozeduralen Programmiersprachen üblich ist.

Die Bedeutung der einzelnen Operanden können Sie folgender Tabelle entnehmen:

Symbol Bedeutung
RTT zuletzt gemessene Round-Trip Time
SRTT Schätzwert der mittleren RTT
ERR (Error) Abweichung zwischen dem Schätzwert der mittleren RTT und der zuletzt gemessenen RTT
DEV (Deviation) Schätzwert der mittleren Abweichung der RTTs von ihrem Mittelwert
Faktor g reelle Zahl zwischen 0 und 1, die steuert, wie stark ein neuer Meßwert der RTT das gewichtete Mittel, d.h. die SRTT, beeinflußt
Faktor h reelle Zahl zwischen 0 und 1, die steuert, wie stark ein neuer Meßwert der RTT die geschätzte mittlere Abweichung, d.h. DEV, beeinflußt
Faktor n steuert, wie stark die geschätzte mittlere Abweichung den RTO beeinflußt
abs(ERR) absoluter Betrag von ERR

Um eine effiziente Berechnung zu ermöglichen, wählt TCP für die Faktoren g und h jeweils Kehrwerte von Zweierpotenzen (Multiplikationen können so durch Verschiebungen ersetzt werden), skaliert intern SRTT und DEV mit dem Faktor 2f und verwendet ausschließlich die Integer-Arithmetik. Folgende Werte sind typisch:

Intern rechnet man also gewöhnlich mit dem Achtfachen der SRTT und dem Vierfachen von DEV, um die durch Multiplikation mit g und h bewirkten Divisionen durch 8 bzw. 4 problemlos im Integer-Bereich ausführen zu können, und zwar durch Verschiebungen um 3 bzw. 2 Stellen nach rechts.

Bei 4.3BSD UNIX wurde der für die Berechnung des RTO genutzte Faktor n auf 2 und bei 4.4BSD UNIX auf 4 gesetzt. Beide Werte sind also auch wieder Zweierpotenzen.

Jedesmal, wenn ein Timeout eintritt, d.h. der Retransmission-Timer abläuft, wird der RTO unabhängig von der SRTT unter Verwendung eines sog. Backoff-Verfahrens erhöht, um sich an die gestiegene Verzögerung im Netz anzupassen und unnötige Paketwiederholungen zu vermeiden, die die Stabilität des Netzes gefährden können. Die Entkopplung von RTO und SRTT ist in dieser Situation sinnvoll, da auf Grund der ausgebliebenen Quittung keine Anpassung der SRTT stattfindet. Sobald die Verzögerung wieder abnimmt, treffen die Bestätigungen wieder vor dem Timeout ein, und der RTO sinkt daher auf den aus der SRTT abgeleiteten Wert.

Die meisten Implementierungen sorgen dafür, daß der RTO nicht uferlos wächst. Manche Systeme begrenzen dazu die Anzahl der möglichen Timeouts pro Segment, wogegen andere einen Gesamt-Timeout (total Timeout) verwalten, der angibt, wie lange der Sender maximal auf die Bestätigung für ein Segment wartet. Trifft bis zum Erreichen der jeweils gewählten Obergrenze trotz Retransmission und Backoff keine Quittung ein, so setzt TCP die Verbindung zurück (RST-Flag). Ein typischer Timeout-Wert beträgt ca. 9 Minuten. Das bedeutet, wenn 9 Minuten nach dem erstmaligen Aussenden eines bestimmten Segments noch immer keine Bestätigung vorliegt, wird die Verbindung durch den Sender abgebrochen.

In der Praxis kommen unterschiedliche Backoff-Algorithmen zum Einsatz. Oft wird ein konstanter Faktor genutzt, d.h., der neue RTO ergibt sich durch einfache Multiplikation des alten RTO mit dem Faktor, der sehr häufig den Wert 2 hat. Manche Implementierungen verwenden mehrere verschiedene, in einer Tabelle gespeicherte Faktoren.

Theoretisch ist die Messung einer RTT ziemlich trivial. Man muß lediglich die Zeitspanne zwischen dem Abschicken eines Segments und dem Eintreffen einer zugehörigen Bestätigung ermitteln. Kommt es allerdings zum Timeout und wird ein Segment mehrfach wiederholt, dann ist nicht klar, ob sich eine eingehende Quittung auf das Original oder eine der nachfolgend verschickten Kopien bezieht.

Dies liegt an den bei TCP verwendeten kumulativen Bestätigungen, die dem Sender nur anzeigen, bis zu welcher Sequenznummer die Daten empfangen wurden, nicht aber, welches Segment quittiert wird. Man spricht deshalb auch von einer Acknowledgement Ambiguity bzw. vom Retransmission Ambiguity Problem. Man könnte nun willkürlich festlegen, welchem Segment die Quittung zuzuordnen ist. Es zeigt sich aber, daß keine der möglichen Varianten ein befriedigendes Resultat erbringt.

TCP löst das Problem der mehrdeutigen Quittungen durch den nach seinem Autor Phil Karn benannten und im Paper Improving Round-Trip Time Estimates in Reliable Transport Protocols beschriebenen Karn's Algorithm. Er ist (genau wie der oben diskutierte Algorithmus von Jacobson zur Bestimmung der RTO) laut RFC 1122 für jede TCP-Implementierung obligatorisch und besagt folgendes:

Wenn eine Quittung für ein Paket eintrifft, das mehrmals gesendet, d.h. mindestens einmal wiederholt wurde, dann ist jede RTT-Messung zu ignorieren, die sich auf dieses Paket bezieht, um so das Retransmission Ambiguity Problem zu vermeiden. Zusätzlich ist der durch ein Backoff-Verfahren für dieses Paket vergrößerte RTO für das nächste Paket aufzubewahren und zu nutzen. Erst wenn dieses oder ein folgendes Paket bestätigt wird, ohne daß zwischendurch eine Retransmission stattgefunden hat, wird der RTO wieder aus der SRTT neu berechnet.

Die SRTT wird also nur durch diejenigen RTT-Messungen beeinflußt, die auf eindeutigen Quittungen basieren. Solange ausschließlich mehrdeutige Quittungen eintreffen, bleibt die SRTT unverändert und dient auch nicht mehr als Basis für die Bestimmung des RTO. Der RTO wird während dieser Zeit nur durch das Backoff erhöht. Würde man auf das Backoff verzichten, bliebe der RTO bis zur nächsten Aktualisierung der SRTT, die erst wieder nach dem Empfang einer eindeutigen Quittung stattfindet, konstant. Da er aber auf Basis der alten SRTT bestimmt wurde, ist er für die durch eine gestiegene Verzögerung charakterisierte neue Situation zwangsläufig zu kurz und verursacht unnötige Paketwiederholungen, die man bekanntlich gerade vermeiden will.

Persist-Timer

Der Empfänger kann den Sender stoppen, indem er die im TCP-Kopf signalisierte Fenstergröße auf Null setzt. Der Sender darf dann so lange keine Daten senden, bis der Empfänger das Fenster wieder öffnet. Die Anzeige der neuen Fenstergröße wird möglicherweise in einem Segment transportiert, das keine Nutzdaten enthält. Derartige Pakete können unbemerkt abhanden kommen, denn es besteht keine Möglichkeit, sie zu quittieren, da sich Quittungen bei TCP nicht auf Segmente, sondern immer auf Sequenznummern im Datenstrom beziehen, so daß der Sender nur auf Segmente schließen kann, die auch Daten enthalten.

Geht nun die Anzeige der neuen Fenstergröße verloren, kann dies einen Deadlock zur Folge haben: Der Sender wartet auf die Erlaubnis zum Senden (d.h. das Öffnen des Fensters, das für ihn immer noch die Länge 0 hat), und der Empfänger erwartet von der Gegenseite neue Daten (weil er ihr ja bereits ein Fenster offeriert hat, das mehr als 0 Bytes umfaßt).

Um einen solchen Deadlock zu vermeiden, nutzt der Sender den Persist-Timer, der ihn in bestimmten, u.U. schrittweise nach einer Backoff-Strategie wachsenden Abständen dazu veranlaßt, von sich aus die aktuelle Fenstergröße beim Empfänger zu erfragen, z.B. aller 60 Sekunden.

Die dafür übertragenen Segmente werden Window Probes genannt. Sie dürfen maximal ein Datenbyte enthalten, das der Empfänger in seiner Antwort allerdings nicht bestätigt, sofern das Fenster weiterhin die Länge null hat. In diesem Fall wird die bereits in der vorausgegangenen Quittung angegebene Bestätigungsnummer wiederholt, die logischerweise gleich der Sequenznummer des einen Datenbytes der Window Probe ist.

TCP sendet Window Probes, solange das Fenster geschlossen bleibt und die Verbindung noch existiert, d.h., es gibt für diese Abfragen weder eine zeitliche noch eine zahlenmäßige Obergrenze.

Keepalive-Timer

Bis jetzt haben wir noch keine Antwort auf die Frage gegeben, was eigentlich passiert, wenn weder vom einen noch vom anderen Ende einer existierenden Verbindung Daten gesendet werden. Die Antwort lautet: Dann findet im Normalfall kein Paketaustausch zwischen den beiden Beteiligten statt. Im Gegensatz zu anderen Protokollen überprüft TCP in der Regel auch nicht von sich aus die Erreichbarkeit des Partners.

Ein solcher Zustand der Inaktivität kann vom Protokoll her unendlich lange andauern. Zwischenzeitliche Ausfälle von Routern oder Leitungen haben dabei keine Auswirkungen, da TCP-Verbindungen generell ausschließlich in den Endsystemen registriert sind. Ohne Datenfluß bleiben sämtliche Netzprobleme unbemerkt, da niemand Quittungen erwartet und so auch keine Timeouts überwacht werden können. Der Crash eines Endsystems zerstört dagegen alle dort registrierten Verbindungen.

Manchmal möchte man für eine bestimmte inaktive Verbindung wissen, ob der zugehörige Partner noch existiert. Das ist primär für Server interessant, die nach dem Absturz eines Klienten bzw. dessen abrupter Abschaltung die für ihn reservierten Ressourcen wieder freigeben möchten. Deshalb verwalten viele Implementierungen einen laut TCP-Spezifikation optionalen Keepalive-Timer. Er sorgt für das Aussenden eines Testpakets, wenn innerhalb eines bestimmten Intervalls weder Daten noch Bestätigungen empfangen wurden und auch keine Daten zu senden sind.

Der Keepalive-Timer ist umstritten. Vielfach wird die Meinung vertreten, daß die Überprüfung der Existenz bzw. Erreichbarkeit der Gegenseite Aufgabe der Anwendung und nicht der Transportschicht ist. Aus folgenden Gründen ist der Timer kein obligatorischer Bestandteil von TCP:

  1. Es besteht die Gefahr, daß eine an sich gut funktionierende Verbindung abgebrochen wird, weil der Partner temporär nicht erreichbar ist. Sendet TCP z.B. das Testpaket gerade dann, wenn ein Router neu bootet, so entsteht der falsche Eindruck, daß der Klient abgestürzt ist.

  2. Die Testpakete verschwenden Bandbreite. Etwas provokatorisch wird gefragt, wer sich dafür interessiert, ob die Verbindung noch in Ordnung ist, wenn sie ohnehin niemand nutzt.

  3. Testnachrichten kosten bei paketbezogener Tarifierung Geld.

RFC 1122 fordert, daß der Keepalive-Timer nur auf ausdrückliche Anweisung der Anwendung wirksam werden darf. Das konkrete Timer-Intervall muß einstellbar sein. Sein Default-Wert darf nicht weniger als 2 Stunden betragen. Bei typischen Implementierungen ist das Keepalive-Intervall ein globaler Wert, der für alle Verbindungen aller Nutzer gilt.

Das Senden normaler Daten führt jeweils zur Reinitialisierung des Keepalive-Timers, d.h., sein Intervall beginnt mit jedem abgeschickten TCP-Segment neu. Nach Ablauf des Timers wird ein Testpaket transferiert, das meist keine Daten enthält, sondern nur die letzte Quittung wiederholt. Dabei sind vier Fälle zu unterscheiden:

  1. Der Partner ist problemlos erreichbar. Dann bestätigt der Empfänger das Testpaket ganz regulär. Der Sender reinitialisiert daraufhin den Timer.

  2. Der Partner ist abgestürzt und aus diesem Grund noch nicht wieder erreichbar. Möglicherweise bootet er gerade. Deshalb erhält der Sender keine Bestätigung für sein Testpaket. Er wiederholt es daher mehrmals in gewissen Abständen, z.B. insgesamt 10 Mal und jeweils aller 75 Sekunden.

  3. Der Partner hat nach einem Absturz neu gebootet. Dann erhält der Sender ein Segment mit RST-Flag, wie wir das weiter oben schon diskutiert hatten.

  4. Der Partner läuft normal, ist aber wegen Netzproblemen nicht erreichbar. Genau wie im 2. Szenario bleibt die Quittung aus. Die beiden Fälle sind allerdings unterscheidbar, wenn der Sender durch ICMP-Meldungen des Typs Destination Unreachable mit dem Code Network unreachable for type of service erfährt, daß das Netz nicht ordnungsgemäß funktioniert.

Wird die Gegenseite vor einem Reboot regulär heruntergefahren, dann schließt sie ihre Hälfte der Verbindung und signalisiert dies durch ein Segment mit gesetztem FIN-Flag. In diesem Fall wird der Keepalive-Timer nicht benötigt, um den Rückzug des Partners zu erkennen.

Unter dem URL http://www.tu-chemnitz.de/~hot/keepalive/keepalive.html finden Sie die Beschreibung eines praktischen Experiments mit dem Keepalive-Timer.


Frage 2.3.8.1:

Was wird höchstwahrscheinlich passieren, wenn Sie mitten in einer TCP-basierten Telnet-Verbindung die Netzverbindung physikalisch trennen ("Stecker abziehen")?


© Holger Trapp, 3.6.1998