2.3.7 Geschlossene Übertragung größerer Datenmengen

Um eine zuverlässige Übertragung realisieren zu können, erwartet TCP für alle ausgesendeten Daten eine Bestätigung durch den Empfänger. Trifft diese nicht innerhalb einer vom Sender festgelegten Zeit ein, so wird die Übertragung wiederholt. Dieses allgemeine Prinzip läßt sich auf verschiedene Weise in konkreten Protokollen umsetzen. Die Frage, wann und wie oft Sendungen wiederholt werden sollten, ist Gegenstand des nächsten Abschnitts. Uns interessiert hier zunächst ein anderer Aspekt, und zwar die effektive Organisation und Steuerung des Datenflusses.

TCP könnte ebenfalls das bei TFTP genutzte Stop-and-Wait-Protokoll verwenden, bei dem jedes Paket erst bestätigt werden muß, bevor das nächste übertragen werden darf. Dieses Protokoll läßt sich zwar sehr leicht implementieren, hat aber den Nachteil, speziell bei Netzen mit großen Verzögerungen einen beträchtlichen Anteil der Netzbandbreite zu verschwenden, da es keinen simultanen Datenfluß in beide Richtungen zuläßt:

In bestimmten Situationen bleibt das Netz sogar völlig ungenutzt. Dies trifft für die Zeit zwischen dem Empfang eines Pakets und dem Aussenden der Bestätigung sowie nach dem Verlust eines Paketes bzw. der zugehörigen Quittung zu. Im Falle des Paket- bzw. Quittungsverlustes wird erst nach Ablauf des Retransmission-Timers und der dadurch ausgelösten Sendewiederholung das nächste Paket gesendet:

Um möglichst einen maximalen Durchsatz zu erreichen, verwendet TCP eine intelligentere und komplexere Technik, die man als Sliding-Window-Protokoll bezeichnet. Der Kerngedanke besteht darin, daß der Sender jeweils mehr als ein Paket aussenden darf, bevor eine Quittung erforderlich wird. Die Quittung wiederum beschränkt sich nicht auf genau ein Paket, sondern kann geschlossen den Empfang von mehreren oder auch allen Paketen bestätigen.

Der Begriff sliding Window besagt, daß jeweils ein bestimmter zusammenhängender Teil das Datenstroms, d.h. eine Bytefolge, von einem sog. Fenster überdeckt wird, das mit fortschreitender Kommunikation von vorn nach hinten durch den Strom gleitet. Der Sender und der Empfänger verwalten jeweils ein eigenes Fenster. Da bei TCP beide Partner Pakete sowohl senden als auch empfangen, existieren folglich insgesamt vier Fenster.

Das Sendefenster gibt die Sequenznummern derjenigen Bytes an, die sofort ausgesendet werden dürfen, ohne auf erst auf eine Bestätigung der zuvor verschickten Daten warten zu müssen. Analog beschreibt das Empfangsfenster die Sequenznummern der Bytes, die der Empfänger aktuell akzeptieren kann. Die folgende Grafik soll das Grundprinzip verdeutlichen:

Ein Transfer von drei Paketen könnte z.B. so aussehen:

Wir erkennen, daß der Sender alle drei Pakete unmittelbar hintereinander verschicken konnte, ohne auf eine Bestätigung warten zu müssen. Die Quittung für Paket 1, d.h. die Bestätigung des Empfangs aller zu Paket 1 gehörenden Bytes trifft erst nach dem Aussenden von Paket 3 ein.

Speziell für das Sendefenster stellt sich die Situation wie folgt dar:

Die bei TCP genutzte Form des Sliding-Window-Protokolls gestattet neben einer effizienten Übertragung auch eine End-zu-End-Flußkontrolle. Der Empfänger teilt dem Sender im Feld Fenstergröße des TCP-Kopfes die aktuelle Größe des Empfangsfensters mit. Diese Angabe ist relativ zur Bestätigungsnummer zu interpretieren und gibt die Maximalanzahl der Bytes an, die der Empfänger noch akzeptieren kann. Sie stellt damit gleichzeitig eine Obergrenze für die Größe des Sendefensters dar. Auf diese Weise kann der Empfänger die übertragene Datenmenge seinen aktuellen Kapazitäten anpassen.

Zieht man von der Gesamtgröße den Teil ab, der schon gesendet, aber noch nicht quittiert wurde, so ergibt sich das für den Sender nutzbare Fenster (usable Window).

Indem der Empfänger nach und nach den Erhalt von Daten bestätigt, gleitet das Sendefenster sukzessive nach rechts, wobei seine Größe sehr stark variieren kann. Die folgenden drei Vorgänge sind zu unterscheiden:

  1. Das Fenster schließt sich, wenn sein linker Rand nach rechts rückt. Dies passiert, wenn eine Quittung für am linken Fensterrand befindliche Daten eintrifft.

  2. Das Fenster öffnet sich, wenn der rechte Fensterrand nach rechts bewegt wird, so daß mehr Daten gesendet werden dürfen. Dies ist der Fall, wenn die auf der Empfängerseite laufende Anwendung Daten liest und dadurch TCP veranlaßt, Teile des Empfangspuffers freizugeben und dem Sender eine neue, inkrementierte Fenstergröße mitzuteilen.

  3. Das Fenster schrumpft, wenn sich sein rechter Rand nach links bewegt. RFC 1122 legt fest, daß ein Empfänger das Schrumpfen des Fensters nicht veranlassen sollte, TCP aber damit sicher umgehen können muß, auch wenn es möglicherweise zu einem nutzbaren Fenster mit negativer Länge führt.
Die folgende Abbildung veranschaulicht die Bewegung der Fensterränder:

Sofern ein ACK eintrifft, das den linken Fensterrand nach links verschieben würde, handelt es sich um ein Duplikat einer schon früher empfangenen Quittung und wird verworfen. Der linke Fensterrand kann sich somit immer nur nach rechts bewegen. Wenn der linke und der rechte Rand des Fensters zusammenfallen, das Fenster also null Byte groß ist, spricht man von einem Zero Window. Dann überträgt der Sender so lange keine weiteren Daten, bis der Empfänger das Fenster wieder vergrößert.

Zur Illustration soll ein snoop-Mitschnitt einer mit sock aufgebauten TCP-Verbindung dienen:

  1   0.00000 ultra.informatik.tu-chemnitz.de -> yang.informatik.tu-chemnitz.de 
      TCP D=7777 S=33075 Syn Seq=3744557062 Len=0 Win=8760
  2   0.00144 yang.informatik.tu-chemnitz.de -> ultra.informatik.tu-chemnitz.de 
      TCP D=33075 S=7777 Syn Ack=3744557063 Seq=1019814988 Len=0 Win=64240
  3   0.00005 ultra.informatik.tu-chemnitz.de -> yang.informatik.tu-chemnitz.de 
      TCP D=7777 S=33075     Ack=1019814989 Seq=3744557063 Len=0 Win=8760
  4   0.00118 ultra.informatik.tu-chemnitz.de -> yang.informatik.tu-chemnitz.de 
      TCP D=7777 S=33075     Ack=1019814989 Seq=3744557063 Len=1460 Win=8760
  5   0.00357 yang.informatik.tu-chemnitz.de -> ultra.informatik.tu-chemnitz.de 
      TCP D=33075 S=7777     Ack=3744558523 Seq=1019814989 Len=0 Win=64240
  6   0.00010 ultra.informatik.tu-chemnitz.de -> yang.informatik.tu-chemnitz.de 
      TCP D=7777 S=33075     Ack=1019814989 Seq=3744558523 Len=1460 Win=8760
  7   0.00012 ultra.informatik.tu-chemnitz.de -> yang.informatik.tu-chemnitz.de 
      TCP D=7777 S=33075     Ack=1019814989 Seq=3744559983 Len=1460 Win=8760
  8   0.08381 yang.informatik.tu-chemnitz.de -> ultra.informatik.tu-chemnitz.de 
      TCP D=33075 S=7777     Ack=3744561443 Seq=1019814989 Len=0 Win=64240
  9   0.00007 ultra.informatik.tu-chemnitz.de -> yang.informatik.tu-chemnitz.de 
      TCP D=7777 S=33075 Fin Ack=1019814989 Seq=3744561443 Len=620 Win=8760
 10   0.03501 yang.informatik.tu-chemnitz.de -> ultra.informatik.tu-chemnitz.de 
      TCP D=33075 S=7777     Ack=3744562064 Seq=1019814989 Len=0 Win=64240
 11   0.01596 yang.informatik.tu-chemnitz.de -> ultra.informatik.tu-chemnitz.de 
      TCP D=33075 S=7777 Fin Ack=3744562064 Seq=1019814989 Len=0 Win=64240
 12   0.00004 ultra.informatik.tu-chemnitz.de -> yang.informatik.tu-chemnitz.de 
      TCP D=7777 S=33075     Ack=1019814990 Seq=3744562064 Len=0 Win=8760
Die Details [slidwin.txt] stehen wieder in einer eigenen Datei zur Verfügung.


Frage 2.3.7.1:

Geben Sie für jeden der beiden Partner getrennt an, wieviele Bytes er mit jedem einzelnen ACK bestätigt.


Treffen Datensegmente ein, die zwar im Empfangsfenster, nicht aber an dessen linkem Rand liegen (out-of-order Segments), sollte TCP sie zwischenspeichern, um möglichst zu vermeiden, daß sie nochmals gesendet werden, da dies den Durchsatz negativ beeinflussen kann. Kommt z.B. von fünf im Datenstrom aufeinanderfolgenden Segmenten das erste abhanden und werden die restlichen vier korrekt empfangen und aufbewahrt, dann genügt u.U. die Retransmission des ersten Pakets, um anschließend den Empfang aller fünf Segmente in einer einzigen Quittung bestätigen zu können.

Generell muß TCP mit jeder Bestätigung soviel Daten wie möglich quittieren, d.h., eine Quittung muß die Bytes aller Segmente umfassen, die am linken Fensterrand lückenlos hintereinander gespeichert sind. Es ist somit nicht zulässig, diese Segmente einzeln zu bestätigen.

In der Regel hat eine Anwendung die Möglichkeit, über die verwendete API die Fenstergröße einzustellen und damit evtl. die Performance zu beeinflussen. Ein Fenster der Länge eins würde unser Sliding-Window-Protokoll zu dem bereits erwähnten einfachen Stop-and-Wait-Protokoll degradieren. Durch eine hinreichende Vergrößerung des Fensters läßt sich allerdings die Zeit, in der das Netz protokollbedingt ungenutzt bleibt, vollständig eliminieren. Das heißt, der Sender kann Pakete so schnell senden, wie sie das Netz zu transportieren vermag.

Daraus folgt, daß ein den Gegebenheiten gut angepaßtes Sliding-Window-Protokoll die Leistung des Netzes voll ausnutzt und somit einen deutlich höheren Durchsatz als ein einfaches Stop-and-Wait-Protokoll erreicht.

Die für einen kontinuierlichen Datenfluß notwendige Fenstergröße wird durch die Kapazität des zwischen Sender und Empfänger bestehenden logischen Kanals bestimmt, d.h. durch das Produkt der in Bits pro Sekunde gemessenen Bandbreite sowie der in Sekunden gemessenen Round-Trip Time (RTT). Dieser Wert wird daher oft als Bandwidth-Delay Product bezeichnet. Bei heutigen Netzen kann er die maximale Fenstergröße von 65535 Bytes schnell übersteigen. Wie wir noch sehen werden, läßt sich diese Begrenzung mit der bereits weiter oben genannten Option Window Scale umgehen.

Wenn Sie sich die detaillierten Mitschnitte des zuletzt gezeigten Beispiels [slidwin.txt] oder der zuvor behandelten Telnet-Verbindung [telnet.txt] etwas näher anschauen, bemerken Sie an verschiedenen Stellen die Verwendung des PSH-Flags (Push), mit dessen Hilfe der Sender den Empfänger auffordert, alle gepufferten Daten unverzüglich an die empfangende Anwendung zu übergeben, also nicht länger auf weitere Daten zu warten.

Ursprünglich ging man davon aus, daß eine Anwendung über die API das Setzen dieses Flags veranlaßt. So könnte z.B. der Klient einer interaktiven Anwendung beim Senden von Zeichen oder Kommandos an den Server das PSH-Flag nutzen, um unangenehme Verzögerungen zu vermeiden, die aus der Pufferung von Daten resultieren.

Die meisten heutzutage verfügbaren Interfaces bieten allerdings keine Möglichkeit zur Beeinflussung des Push-Flags. Es bleibt daher TCP überlassen, dieses Flag geeignet zu verwenden. Die meisten vom Berkeley-Code abgeleiteten Implementierungen setzen PSH automatisch, wenn durch das Senden eines Segments der gesamte Sendepuffer geleert wird. Empfängerseitig ignorieren sie das Flag, da sie generell die Datenübergabe an die Anwendungen nicht verzögern.

Zum Abschluß dieses Abschnitts wenden wir uns noch einmal dem Thema Vorrangdaten zu. Es gehört laut Comer und Stevens zu den am wenigsten dokumentierten und verstandenen Teilen der TCP-Spezifikation und läßt speziell für Implementatoren verschiedene Fragen offen. Wir wollen dennoch die wichtigsten Punkte sowie ein Anwendungsbeispiel diskutieren.

Durch das URG-Flag und den Urgent Pointer zeigt der Sender dem Empfänger an, daß ein bestimmter, theoretisch unbegrenzt langer Bereich des Datenstroms besonders dringend (urgent) ist und deshalb vorrangig, d.h. außer der Reihe behandelt werden sollte, unabhängig von dessen Position. Der Urgent Pointer kennzeichnet immer das Ende der Vorrangdaten. Es gibt keine Möglichkeit, deren Anfang zu spezifizieren.

Der Begriff Urgent Data besagt nicht, daß dringliche Daten gegenüber dem normalen Datenstrom bevorzugt transportiert werden oder daß für sie ein logisch separater Datenkanal existiert. Die Anzeige von Vorrangdaten durch ein Segment mit gesetztem URG-Flag ist allerdings jederzeit möglich und kann früher gesendet werden als die zugehörigen Daten selbst.

TCP muß eine Anwendung bei Bedarf asynchron (z.B. unter UNIX durch ein Signal) informieren, wenn

  1. ein gültiger Urgent Pointer eintrifft und die durch ihn signalisierten Vorrangdaten nicht bereits durch einen früher empfangenen Urgent Pointer als solche gekennzeichnet worden sind, oder

  2. sich das bisherige Ende der Vorrangdaten nach hinten verschiebt.

Alles Weitere obliegt der Anwendung. Es bleibt ihr überlassen, ob, wann und wie sie auf Urgent Data reagiert. Z.B. ignorieren TCP-basierte UNIX-Programme standardmäßig alle Vorrangdaten. Um das Konzept nutzen zu können, sind programmseitig spezielle Vorkehrungen nötig. Diejenigen Anwendungen, die Vorrangdaten gezielt einsetzen (z.B. Telnet, Rlogin und FTP), lesen sie in der Regel mit minimaler Verzögerung. Man sagt auch, die Programme wechseln dazu in den sog. Urgent Mode. Über die API können sie, so wie das im RFC 1122 gefordert ist, das Erreichen des Endes der Vorrangdaten erkennen.

Die asynchrone Benachrichtigung der Anwendung wird manchmal auch Out-of-Band-Signalisierung genannt und gestattet, Steuerkommandos so schnell wie möglich an Anwendungen zu schicken, auch dann, wenn das aktuelle Fenster die Länge null hat (Zero Window), so daß keine regulären Daten gesendet werden können.

Ein Beispiel finden wir bei Rlogin. Wenn hier das auf dem Server laufende Programm abgebrochen wird, soll der Klient umgehend angewiesen werden, alle noch im Netz gepufferten Daten zu verwerfen. Deshalb schickt der Server Vorrangdaten, die genau das Kommando Flush Output beinhalten. Nachdem der Klient seitens TCP über die Existenz dieser Vorrangdaten informiert wurde, wechselt er bei der nächsten Gelegenheit in den Urgent Mode, liest alle Daten bis einschließlich des Kommandos und führt dieses anschließend aus, d.h., die zuvor gelesenen Bytes werden einfach vernichtet.

Da Vorrangdaten jederzeit signalisiert werden können, funktioniert der beschriebene Mechanismus auch dann, wenn der Klient gerade keine normalen Daten akzeptiert, weil sein TCP-Empfangspuffer voll ist. Durch den Beginn des Lesens im Urgent Mode wird der Puffer geleert und das bisher null Byte lange Fenster geöffnet, so daß wieder ein Datenfluß vom Server zum Klienten in Gang kommt.

Silly Window Syndrome

Basiert die Flußsteuerung wie bei TCP auf einem Sliding-Window-Protokoll, so kann die zusammenhängende Übertragung großer Datenmengen bei einfachen Implementierungen einen als Silly Window Syndrome (SWS) bezeichneten Zustand hervorrufen, in dem jede Quittung nur ein sehr kleines Fenster anbietet, obwohl genug Puffer vorhanden ist, und jedes Segment nur sehr wenig Daten enthält. Dadurch verschwendet man Netzbandbreite und verursacht zusätzliche Rechenlast auf den Routern und Endsystemen.

Das SWS kann von beiden Partnern verursacht werden: entweder dadurch, daß der Empfänger nicht wartet, bis genug Puffer zur Verfügung steht, um ein großes Fenster anbieten zu können, oder weil der Sender nicht wartet, bis genug Daten für ein großes Segment zusammengekommen sind. Zur Vermeidung der Probleme müssen folglich beide Seiten zusammen beitragen. Die Lösung ist nicht kompliziert:

  1. Der Empfänger darf keine kleinen Fenster anbieten. Er teilt dem Sender deshalb erst dann ein größeres Fenster mit, wenn die Zunahme gegenüber der bisherigen Länge (die auch null sein kann) entweder mindestens ein Segment maximaler Größe (MSS) oder die Hälfte des Empfangspuffers umfaßt, wobei jeweils das Minimum der beiden Werte gewählt wird.

  2. Der Sender verhindert das SWS durch den schon diskutierten Nagle-Algorithmus. Er initiiert in der Regel nur dann eine Datenübertragung, wenn mindestens eine der folgenden Bedingungen erfüllt ist:


© Holger Trapp, 15.6.1998