Transportschicht

Werbung
K APITEL 3
Transportschicht
3.1 Dienste und Prinzipien auf der Transportschicht
Zwischen der Anwendungs- und der Vermittlungsschicht angesiedelt, bildet die
Transportschicht das Kernstück der geschichteten Netzwerkarchitektur. Sie erfüllt
die wichtige Rolle der direkten Bereitstellung von Kommunikationsdiensten für die
Anwendungsprozesse, die auf verschiedenen Hosts laufen. In diesem Kapitel
beschreiben wir die Dienste, die von einem Protokoll der Transportschicht bereitgestellt werden können, und die verschiedenen, der Bereitstellung dieser Dienste
zugrunde liegenden Prinzipien. Ferner wird beschrieben, wie diese Dienste in bestehenden Protokollen implementiert sind. Wie bisher, liegt die besondere Betonung auf
den Internet-Protokollen, d. h. den TCP- und UDP-Protokollen auf der Transportschicht.
In den ersten beiden Kapiteln haben wir die Rolle der Transportschicht und die
von ihr bereitgestellten Dienste kurz angesprochen. Hier folgt nun eine kurze Übersicht dessen, was Sie über die Transportschicht bisher erfahren haben.
Ein Protokoll der Transportschicht bietet eine logische Kommunikation zwischen
Anwendungsprozessen, die auf unterschiedlichen Hosts laufen. Unter logischer
Kommunikation verstehen wir, dass die kommunizierenden Anwendungsprozesse
zwar nicht physisch miteinander verbunden sind (tatsächlich können sie sich an entgegengesetzten Stellen der Erde befinden und über zahlreiche Router und viele verschiedene Verbindungsleitungen verbunden sein), aus Sicht der Anwendung aber
physikalisch verbunden erscheinen. Anwendungsprozesse verwenden die von der
Transportschicht bereitgestellte logische Kommunikation, um Nachrichten miteinander auszutauschen, ohne sich um die Details der physikalischen Infrastruktur, über
die diese Nachrichten fließen, kümmern zu müssen. Abbildung 3.1 stellt das Konzept
der logischen Kommunikation dar.
Wie aus Abbildung 3.1 deutlich wird, werden Protokolle auf der Transportschicht
in den Endsystemen und nicht in Netzwerk-Routern implementiert. Netzwerk-Router agieren lediglich in den Feldern der 3-PDUs der Vermittlungsschicht; sie agieren
nicht als Felder der Transportschicht.
Auf der sendenden Seite konvertiert die Transportschicht die Nachrichten, die
von einem sendenden Anwendungsprozess ankommen, in 4-PDUs (d. h. in Protokolldateneinheiten der Transportschicht). Dies wird (möglicherweise) dadurch
bewerkstelligt, dass die Nachrichten einer Anwendung in kleinere Stücke aufgeteilt
und jedem Stück ein Header der Transportschicht angehängt wird, um 4-PDUs zu
erzeugen. Die Transportschicht gibt die 4-PDUs dann an die Vermittlungsschicht weiter, wo jede 4-PDU in einer 3-PDU verkapselt wird. Auf der empfangenden Seite
Kapitel 3 – Transportschicht
176
Abbildung 3.1 Die Transportschicht bietet eine logische und keine physikalische
Kommunikation zwischen Anwendungen.
Anwendung
Transport
Vermittl.
Sicherung
Bitübertr.
Lokaler ISP
Vermittl.
Sicherung
Vermittl.
Bitübertr.
Sicherung
Bitübertr.
Vermittl.
gis
Lo
Sicherung
ch
Bitübertr.
Vermittl.
er
Sicherung
En
-E
-zu
de
Bitübertr.
ra
e-T
nd
Vermittl.
rt
po
ns
Sicherung
Bitübertr.
Anwendung
Transport
Basisstation
Firmennetzwerk
Vermittl.
Sicherung
Bitübertr.
erhält die Transportschicht die 4-PDUs von der Vermittlungsschicht, entfernt den
Transport-Header von den 4-PDUs, setzt die Nachrichten wieder zusammen und gibt
sie an einen empfangenden Anwendungsprozess weiter.
Ein Computernetzwerk kann Netzwerkanwendungen mehr als ein Protokoll auf
der Transportschicht zur Verfügung stellen. Das Internet hat beispielsweise zwei Protokolle: TCP und UDP. Jedes dieser Protokolle bietet andere Transportschichtdienste
für die nutzende Anwendung.
Alle Protokolle der Transportschicht bieten einer Anwendung einen Multiplex/
Demultiplex-Dienst. Dieser Dienst wird ausführlich im nächsten Abschnitt beschrieben. Wie in Abschnitt 2.1 erwähnt, kann ein Transportprotokoll abgesehen vom Multiplex/Demultiplex-Dienst auch weitere Dienste, z. B. zuverlässigen Datentransfer
und Zusicherungen über Bandbreiten und Verzögerungen, bereitstellen.
3.1 Dienste und Prinzipien auf der Transportschicht
3.1.1 Beziehung zwischen der Transport- und der
Vermittlungsschicht
Die Transportschicht liegt unmittelbar oberhalb der Vermittlungsschicht im Protokollstack. Während ein Protokoll der Transportschicht eine logische Kommunikation
zwischen Prozessen bietet, die auf unterschiedlichen Hosts laufen, stellt ein Protokoll
der Vermittlungsschicht eine logische Kommunikation zwischen Hosts bereit. Dieser
Unterschied ist subtil, aber sehr wichtig. Wir wollen ihn im Folgenden anhand einer
Haushaltsanalogie erklären.
Man stelle sich zwei Häuser vor, von denen eines an der Ostküste und das andere
an der Westküste (der USA) steht; in jedem der beiden Häuser wohnen ein Dutzend
Kinder. Die Kids in dem Haushalt an der Ostküste sind Cousins der Kids im Haushalt an der Westküste. Die Kids der beiden Haushalte haben Spaß, sich gegenseitig zu
schreiben; jedes Kind schreibt jedem Cousin jede Woche, wobei jeder Brief über die
konventionelle gelbe Post in einem getrennten Umschlag befördert wird. Folglich
verschickt jeder Haushalt pro Woche 144 Briefe an den anderen Haushalt. (Diese Kids
würden viel Geld sparen, wenn sie E-Mail hätten!) In jedem Haushalt ist eines der
Kinder – Ann im Haus an der Westküste und Bill in dem Haus an der Ostküste – für
die Sammlung und die Verteilung der Post zuständig. Ann besucht jede Woche ihre
Brüder und Schwestern, sammelt die Post ein und gibt sie dem Briefträger bei seiner
täglichen Runde. Wenn Briefe im Haus an der Westküste ankommen, hat Ann auch
die Aufgabe, die Post an ihre Brüder und Schwestern zu verteilen. An der Ostküste
führt Bill diese Aufgabe aus.
In diesem Beispiel bietet der Postdienst eine logische Kommunikation zwischen
den beiden Häusern: Er befördert Post von Haus zu Haus und nicht von Person zu
Person. Andererseits bieten Ann und Bill eine logische Kommunikation unter den
Cousins: Sie sammeln die Post von ihren Geschwistern ein und verteilen ankommende Post an sie. Aus Sicht der Cousins sind Ann und Bill der Postdienst, obwohl
die beiden nur ein Teil (der Endsystemteil) des Ende-zu-Ende-Transportprozesses
sind. Dieses Haushaltsbeispiel dient als Analogie, um den Zusammenhang zwischen
der Transport- und der Vermittlungsschicht zu erklären:
Hosts (auch Endsysteme genannt) = Häuser
Prozesse = Cousins
Anwendungsnachrichten = Briefe in Umschlägen
Protokoll der Netzwerkschicht = Postdienst (mit Briefträgern)
Protokoll der Transportschicht = Ann und Bill
Wir fahren mit dieser Analogie fort und stellen fest, dass Ann und Bill ihre gesamte
Arbeit innerhalb ihres jeweiligen Heims verrichten; sie haben z. B. nichts mit dem
Sortieren von Post in einem Postamt oder der Beförderung von Post von einer Postverteilerstelle zu einer anderen zu tun. Ähnlich leben die Protokolle der Transportschicht in den Endsystemen. Innerhalb eines Endsystems verschiebt ein Transportprotokoll Nachrichten von Anwendungsprozessen zur Netzwerkperipherie (d. h. zur
Vermittlungsschicht) und umgekehrt; es hat aber keinen Einfluss darauf, wie die
Nachrichten innerhalb des Netzwerkkerns verschoben werden. Wie aus Abbildung
3.1 deutlich wird, wirken die dazwischen liegenden Router weder auf Informationen
ein, die auf der Transportschicht möglicherweise an die Anwendungsnachrichten
angehängt werden, noch erkennen sie solche.
177
178
Kapitel 3 – Transportschicht
Wir greifen wieder unsere Familiensaga auf und nehmen an, dass zwei andere Cousins, sagen wir Susan und Harvey, Ann und Bill ablösen, wenn diese in den Ferien
sind. Zum Leidwesen der beiden Familien sammeln und verteilen Susan und Harvey
die Post nicht genau auf die gleiche Weise wie Ann und Bill. Susan und Harvey sind
kleinere Kinder und so passiert es schon mal, dass sie Post weniger regelmäßig abholen und verteilen und auch mal einige Briefe verlieren (die manchmal vom Haushund zerfetzt werden). Susan und Harvey bieten also nicht die gleichen Dienste (d. h.
das gleiche Dienstmodell) wie Ann und Bill. Vergleichbar damit kann ein Computernetzwerk mehrere Transportprotokolle zur Verfügung stellen, die sich hinsichtlich
ihres Dienstmodells für Anwendungen unterscheiden.
Die möglichen Dienste, die Ann und Bill bereitstellen können, sind deutlich auf
die möglichen Dienste eingeschränkt, die der Postdienst bietet. Wenn der Postdienst
beispielsweise keine Höchstgrenze festlegt, wie lange die Zustellung eines Briefs zwischen den beiden Häuser dauern darf (z. B. drei Tage), besteht für Ann und Bill keine
Möglichkeit, eine maximale Verzögerung für die Postzustellung zwischen zwei Cousins zuzusichern. Ähnlich werden die Dienste, die ein Transportprotokoll bereitstellen kann, oft durch das Dienstmodell des zugrunde liegenden Protokolls auf der Vermittlungsschicht beschränkt. Wenn das Protokoll auf der Vermittlungsschicht keine
Zusicherungen über Verzögerung oder Bandbreite für 4-PDUs, die zwischen zwei
Hosts versendet werden, machen kann, dann kann das Protokoll auf der Transportschicht keine Zusicherungen über Verzögerung oder Bandbreite für die zwischen
Prozessen ausgetauschten Nachrichten geben.
Dennoch können bestimmte Dienste von einem Transportprotokoll geboten werden, auch wenn das zugrunde liegende Netzwerkprotokoll auf der Vermittlungsschicht keinen entsprechenden Dienst bietet. Wie wir in diesem Kapitel noch sehen
werden, kann ein Transportprotokoll einer Anwendung z. B. zuverlässigen Datentransferdienst bieten, auch wenn das zugrunde liegende Netzwerkprotokoll unzuverlässig ist, d. h. auch wenn das Netzwerkprotokoll Pakete verliert, verstümmelt
und dupliziert. Als weiteres Beispiel (das wir umfassender in Kapitel 7 in Zusammenhang mit Netzwerksicherheit behandeln) kann ein Transportprotokoll Verschlüsselung anwenden, um zuzusichern, dass Nachrichten nicht von Eindringlingen gelesen werden, auch wenn die Vermittlungsschicht keine Vertraulichkeit von 4-PDUs
zusichern kann.
3.1.2 Übersicht über die Transportschicht im Internet
Wir erinnern uns, dass das Internet, und allgemeiner ein TCP/IP-Netzwerk, der
Anwendungsschicht zwei unterschiedliche Protokolle auf der Transportschicht zur
Verfügung stellt. Eines dieser Protokolle ist UDP (User Datagram Protocol), das
Anwendungen einen unzuverlässigen, verbindungslosen Dienst bereitstellt. Das
zweite ist TCP (Transmission Control Protocol), das Anwendungen einen zuverlässigen, verbindungsorientierten Dienst bietet. Im Design einer Netzwerkanwendung
muss der Anwendungsentwickler eines dieser beiden Transportprotokolle spezifizieren. Wir haben in den Abschnitten 2.6 und 2.7 gesehen, dass der Anwendungsentwickler entweder UDP oder TCP wählt, wenn er Sockets erstellt.
Um die Terminologie zu vereinfachen, sprechen wir in Zusammenhang mit dem
Internet von einem Segment, um eine 4-PDU zu bezeichnen. In der Internet-Literatur
(z. B. in RFCs) wird die PDU in Zusammenhang mit TCP oft als »Segment« und in
Zusammenhang mit UDP als Datagramm bezeichnet. In der gleichen Literatur findet
3.1 Dienste und Prinzipien auf der Transportschicht
man aber auch den Begriff »Datagramm« für PDUs der Vermittlungsschicht! Wir sind
der Ansicht, dass in einem Fachbuch über die Grundlagen im Vernetzungsbereich
wie diesem keine Verwirrung mit Begriffen gestiftet werden soll, und verwenden
daher den Begriff »Segment« für TCP- und UDP-PDUs, während wir den Begriff
»Datagramm« nur für PDUs der Vermittlungsschicht verwenden.
Bevor wir mit unserer kurzen Einführung von UDP und TCP fortfahren, sind an
dieser Stelle ein paar Worte über die Vermittlungsschicht des Internets nützlich. (Die
Vermittlungsschicht ist Thema von Kapitel 4.) Das Protokoll der Internet-Vermittlungsschicht ist IP (Internet Protocol). IP bietet eine logische Kommunikation zwischen Hosts. Das IP-Dienstmodell ist der Best-Effort-Dienst. Das bedeutet, dass IP
Segmente zwischen kommunizierenden Hosts nach »bestem Bemühen« überträgt,
allerdings keine Zusicherungen macht. Insbesondere gibt es keine Zusicherung über die
Ankunft von Segmenten, über deren Ankunft in der richtigen Reihenfolge und über
die Integrität der Daten in den Segmenten. Folglich bietet IP einen unzuverlässigen
Dienst. Jeder Host hat eine IP-Adresse. IP-Adressierung wird ausführlich in Kapitel 4
behandelt; vorläufig genügt es zu wissen, dass jeder Host eine eindeutige IP-Adresse
hat.
Nachdem wir einen Blick auf das IP-Dienstmodell geworfen haben, fassen wir das
Dienstmodell von UDP und TCP zusammen. Die grundlegende Verantwortung von
UDP und TCP ist im Wesentlichen die Erweiterung des IP-Übertragungsdienstes zwischen zwei Endsystemen auf einen Übertragungsdienst zwischen zwei Prozessen, die
auf zwei Endsystemen laufen. Diese Erweiterung der Host-zu-Host- auf die Prozesszu-Prozess-Übertragung wird als Anwendungsmultiplexen und -demultiplexen
bezeichnet (wird im nächsten Abschnitt beschrieben). UDP und TCP bieten auch
Integritätsprüfung dadurch, dass Fehlererkennungsfelder in die Header einbezogen
werden. Diese beiden minimalen Transportschichtdienste – Prozess-zu-ProzessDatenübertragung und Fehlerprüfung – sind die einzigen Dienste, die UDP bereitstellt! Wie IP ist UDP ein unzuverlässiger Dienst; es gibt keine Zusicherung, dass die
von einem Prozess gesendeten Daten beim Zielprozess intakt ankommen. UDP wird
ausführlich in Abschnitt 3.3 behandelt.
TCP dagegen bietet Anwendungen mehrere zusätzliche Dienste. Vor allem bietet
es zuverlässigen Datentransfer. Mit Hilfe von Flusskontrolle, Sequenznummern,
Bestätigungen (ACKs) und Timern (Techniken, die in diesem Kapitel ausführlich
behandelt werden) gewährleistet TCP, dass die Daten vom sendenden zum empfangenden Prozess korrekt und in der richtigen Reihenfolge übertragen werden. TCP
konvertiert folglich den unzuverlässigen Dienst von IP zwischen Endsystemen in
einen zuverlässigen Datentransportdienst zwischen Prozessen. TCP nutzt auch Überlastkontrolle. Dabei handelt es sich nicht so sehr um einen Dienst für die aufrufende
Anwendung, als vielmehr um einen für das Internet insgesamt, also einen Dienst
zum allgemeinen Nutzen. Grob gesagt, hindert die TCP-Überlastkontrolle eine TCPVerbindung daran, die Verbindungsleitungen und Switches zwischen kommunizierenden Hosts mit übermäßigem Verkehrsvolumen zu überschwemmen. Im Prinzip
können TCP-Verbindungen, die ein überlastetes Netzwerk überqueren, die Bandbreite der betreffenden Verbindungsleitung gemeinsam nutzen. Dies wird dadurch
bewerkstelligt, dass die Rate, in der die sendende Seite Verkehr in das Netzwerk einspeisen kann, reguliert wird. Im Gegensatz dazu wird UDP-Verkehr nicht reguliert.
Eine Anwendung, die UDP-Transport nutzt, kann in jeder beliebigen Rate senden,
solange sie will.
179
180
Kapitel 3 – Transportschicht
Ein Protokoll, das zuverlässigen Datentransfer und Überlastkontrolle bietet, ist natürlich komplex. Wir müssen die Prinzipien von zuverlässigem Datentransfer und Überlastkontrolle in mehreren Abschnitten beschreiben und zusätzliche Abschnitte sind
notwendig, um das TCP-Protokoll selbst abzudecken. Diese Themen werden in den
Abschnitten 3.4 bis 3.8 behandelt. In diesem Kapitel wechseln wir zwischen einem
bestimmten Basisprinzip und dem TCP-Protokoll. Beispielsweise behandeln wir
zuerst den zuverlässigen Datentransfer im Allgemeinen und anschließend die Art,
wie TCP im Besonderen zuverlässigen Datentransfer bereitstellt. Ebenso behandeln
wir Überlastkontrolle zuerst im allgemeinen Umfeld und anschließend spezifisch in
TCP. Bevor wir uns diesen Themen zuwenden, betrachten wir im nächsten Abschnitt
zunächst das Multiplexen und Demultiplexen von Anwendungen.
3.2 Multiplexen und Demultiplexen von Anwendungen
Dieser Abschnitt befasst sich mit dem Multiplexen und Demultiplexen von Netzwerkanwendungen. Um eine konkrete Beschreibung sicherzustellen, befassen wir uns
mit diesem grundlegenden Transportschichtdienst in Zusammenhang mit dem Internet. Wir betonen allerdings, dass für alle Computernetzwerke ein Multiplex/Demultiplex-Dienst erforderlich ist.
Der Multiplex/Demultiplex-Dienst zählt zwar nicht zu den interessantesten
Diensten, die ein Protokoll der Transportschicht bereitstellen kann, er ist jedoch absolut wichtig. Um dies zu verstehen, bedenke man die Tatsache, dass IP Daten zwischen zwei Endsystemen überträgt, wobei jedes Endsystem mit einer eindeutigen IPAdresse identifiziert wird. IP überträgt Daten nicht zwischen den Anwendungsprozessen, die auf diesen Endsystemen laufen. Die Erweiterung der Host-zu-Host- auf
die Prozess-zu-Prozess-Übertragung wird durch Anwendungsmultiplexen und
-demultiplexen erreicht.
Auf dem Zielhost empfängt die Transportschicht Segmente (d. h. PDUs der Transportschicht) von der unmittelbar darunter liegenden Vermittlungsschicht. Die Transportschicht ist für die Übertragung der Daten in diesen Segmenten an den entsprechenden Anwendungsprozess, der auf dem Host läuft, zuständig. Wir betrachten ein
Beispiel. Angenommen, Sie sitzen vor Ihrem Computer und laden Web-Seiten herunter, während Sie eine FTP-Sitzung und zwei Telnet-Sitzungen ausführen. Das heißt, es
laufen momentan vier Netzwerkanwendungsprozesse (zwei Telnet-Prozesse, ein
FTP-Prozess und ein HTTP-Prozess). Wenn die Transportschicht in Ihrem Computer
Daten von der darunter liegenden Vermittlungsschicht empfängt, muss sie die empfangenen Daten an einen dieser vier Prozesse weiterleiten. Wie geht sie dabei vor?
Jedes Transportschichtsegment umfasst eine Reihe von Feldern, die den Prozess
bestimmen, an den die Daten des Segments zu übertragen sind. Am empfangenden
Ende kann die Transportschicht dann diese Felder prüfen, um den empfangenden
Prozess zu ermitteln und das Segment an diesen Prozess weiterzuleiten. Die Aufgabe
der Übertragung der in einem Transportschichtsegment enthaltenen Daten an den
richtigen Anwendungsprozess nennt man Demultiplexen. Die Aufgabe des Einsammelns von Daten im Quellhost aus verschiedenen Anwendungsprozessen, die Vervollständigung der Daten mit Header-Informationen (die später beim Demultiplexen
benutzt werden), um Segmente zu bilden, und die Weiterleitung der Segmente an die
Vermittlungsschicht wird als Multiplexen bezeichnet. Multiplexen und Demultiplexen sind in Abbildung 3.2 dargestellt.
3.2 Multiplexen und Demultiplexen von Anwendungen
181
Abbildung 3.2 Multiplexen und Demultiplexen
Empfänger
Daten auf der
Anwendungsschicht
P1
SegmentM
Header
Anwendung
Segment
Ht
M
Hn Segment
P3
P4
M
M'
Anwendung
P2
Transport
M'
Vermittl.
Anwendung
Transport
Transport
Vermittl.
Vermittl.
Um Demultiplexen besser zu verstehen, betrachten wir wieder unsere Haushaltssaga
aus dem vorherigen Abschnitt. Jedes der Kids wird anhand seines Namens unterschieden. Wenn Bill einen Stapel Post vom Briefträger erhält, führt er eine Demultiplexoperation durch, indem er feststellt, an wen die Briefe adressiert sind, und die Post
dann an seine Brüder und Schwestern verteilt. Ann führt eine Multiplexoperation
durch, wenn sie Briefe von ihren Geschwistern einsammelt und die angesammelte
Post dem Briefträger übergibt.
UDP und TCP führen die Demultiplex- und Multiplexaufgaben dadurch aus, dass
sie zwei spezielle Felder in die Segment-Header einbeziehen: das Feld Portnummer
Quelle und das Feld Portnummer Ziel. Diese beiden Felder sind in Abbildung 3.3
dargestellt. Zusammen identifizieren die beiden Felder eindeutig einen Anwendungsprozess, der auf dem Zielhost läuft. (UDP- und TCP-Segmente haben noch weitere Felder, die in den nächsten Abschnitten dieses Kapitels beschrieben werden.)
Abbildung 3.3 Felder für die Portnummer von Quelle und Ziel in einem Segment der
Transportschicht
32 Bit
Portnummer Quelle Portnummer Ziel
Weitere Header-Felder
Anwendungsdaten
(Nachricht)
182
Kapitel 3 – Transportschicht
Das Konzept von Portnummern wurde in den Abschnitten 2.6 und 2.7 in Zusammenhang mit der Anwendungsentwicklung und der Socket-Programmierung kurz vorgestellt. Die Portnummer ist eine 16-Bit-Nummer von 0 bis 65535. Die Portnummern im
Bereich von 0 bis 1023 werden als wohl bekannte (well-known) Portnummern
bezeichnet und sind eingeschränkt, was bedeutet, dass sie für wohl bekannte Anwendungsprotokolle wie HTTP und FTP reserviert sind. HTTP benutzt Portnummer 80
und FTP Portnummer 21. RFC 1700 enthält eine Liste aller wohl bekannten Portnummern. Wenn wir eine neue Anwendung (z. B. eine der Anwendungen in den Abschnitten 2.6 bis 2.8) entwickeln, müssen wir der Anwendung eine Portnummer zuweisen.
Angesichts der Tatsache, dass jeder Anwendungstyp, der auf einem Endsystem
läuft, eine eindeutige Portnummer hat, stellt sich die Frage, warum das Transportschichtsegment Felder für zwei Portnummern – eines für die Portnummer der Quelle
und eines für die des Ziels – beinhaltet? Die Antwort ist einfach: Ein Endsystem kann
zwei Prozesse des gleichen Typs gleichzeitig ausführen; deswegen genügt die Zielportnummer einer Anwendung nicht immer, um einen bestimmten Prozess zu identifizieren. Dies ist z. B. der Fall, wenn ein Web-Server für jede verarbeitete Anfrage
einen neuen HTTP-Prozess startet. Jedes Mal, wenn dieser Web-Server mehr als eine
Anfrage bedient (was keinesfalls ungewöhnlich ist), führt der Server mehr als einen
Prozess mit Portnummer 80 aus. Um den Prozess, an den Daten gerichtet sind, eindeutig zu identifizieren, ist deshalb eine zweite Portnummer erforderlich.
Wie wird diese zweite Portnummer erzeugt? Welche Portnummer wird im Feld
»Portnummer Quelle« eines Segments angegeben? Welche wird im Feld »Portnummer Ziel« eines Segments angegeben? Um diese Fragen zu beantworten, rufen wir
uns aus Abschnitt 2.1 wieder ins Gedächtnis, dass Netzwerkanwendungen rund um
das Client/Server-Modell organisiert sind. Normalerweise ist der Host, der die
Anwendung einleitet, der Client, und der andere Host ist der Server. Wir betrachten
ein spezifisches Beispiel. Angenommen, die Anwendung hat Portnummer 23 (diejenige für Telnet). Ein Transportschichtsegment verlässt den Client (d. h. den Host, der
die Telnet-Sitzung gestartet hat) in Richtung Server. Wie lautet bei diesem Segment
die Portnummer für die Quelle und das Ziel? Die Zielportnummer dieses Segments
ist die der Anwendung, nämlich 23. Für die Quellportnummer benutzt der Client
eine Nummer, die noch keinem anderen Hostprozess zugewiesen wurde. (Dies
erfolgt automatisch durch die Transportschichtsoftware, die auf dem Client läuft, und
ist für den Anwendungsentwickler transparent.) Es sei gegeben, dass der Client Portnummer x wählt. Jedes Segment, das dieser Prozess an den Telnet-Server sendet, enthält als Quellportnummer x und als Zielportnummer 23. Wenn das Segment beim
Server ankommt, ermöglichen es die beiden Portnummern im Segment dem Serverhost, die Daten des Segments an den richtigen Anwendungsprozess weiterzuleiten.
Die Zielportnummer 23 identifiziert einen Telnet-Prozess und die Quellportnummer
x den spezifischen Telnet-Prozess.
Die Situation ist umgekehrt bei den Segmenten, die vom Server zum Client fließen. Die Quellportnummer ist jetzt die Anwendungsportnummer, also 23, und die
Zielportnummer ist jetzt x (die gleiche x, die als Quellportnummer für die Segmente,
die vom Client zum Server gesendet wurden, benutzt wird). Wenn ein Segment beim
Client ankommt, ermöglichen es die Quell- und Zielportnummern im Segment dem
Clienthost, die Daten des Segments an den richtigen Anwendungsprozess weiterzugeben, der durch das Portnummernpaar identifiziert wird (siehe Abbildung 3.4).
Sie fragen sich jetzt vielleicht, was passiert, wenn zwei verschiedene Clients eine
Sitzung zu einem Server aufbauen und jeder der beiden Clients die gleiche Quellport-
3.2 Multiplexen und Demultiplexen von Anwendungen
Abbildung 3.4 Verwendung einer Quell- und Zielportnummer in einer Client/ServerAnwendung
Quellport: x
Host A
Zielport: 23
Server B
Quellport: 23
Zielport: x
nummer x wählt? Dies kann auf einem stark frequentierten WWW-Server, der viele
Web-Clients gleichzeitig bedient, tatsächlich passieren. Wie kann der Server die Segmente demultiplexen, wenn die beiden Sitzungen genau das gleiche Portnummernpaar haben? Die Antwort auf diese Frage lautet, dass der Server auch die IP-Adressen
in den IP-Datagrammen, in denen diese Segmente befördert werden, heranzieht. (IPDatagramme und Adressierung werden ausführlich in Kapitel 4 behandelt.) Diese
Situation ist in Abbildung 3.5 dargestellt, bei der Host C zwei HTTP-Sitzungen zu
Server B und Host A eine HTTP-Sitzung zu B einleitet.
Abbildung 3.5 Zwei Clients verwenden die gleichen Portnummern, um mit der gleichen
Server-Anwendung zu kommunizieren.
WWW-ClientHost C
Quell-IP: C
Quell-IP: C
Ziel-IP: B
Ziel-IP: B
Quellport: y
Quellport: x
Zielport: 80
Zielport: 80
WWW-ClientHost A
Quell-IP: A
Ziel-IP: B
Quellport: x
Zielport: 80
WWWServer B
183
184
Kapitel 3 – Transportschicht
Die Hosts A und C und Server B haben jeweils eine eindeutige IP-Adresse – A, C bzw.
B. Host C weist den beiden HTTP-Verbindungen, die von Host A ausgehen, jeweils
eine unterschiedliche Quellportnummer (die SP-Nummer x bzw. y) zu. Da Host A die
Quellportnummern aber unabhängig von C wählt, kann es passieren, dass er seiner
HTTP-Verbindung ebenfalls SP = x zuweist. Server B wäre dennoch in der Lage, die
beiden Verbindungen korrekt zu demultiplexen, weil die beiden Verbindungen je
eine andere IP-Quelladresse haben. Zusammenfassend kann man sagen: Wenn ein
Zielhost Daten von der Vermittlungsschicht empfängt, wird das Trio (IP-Quelladresse, Quellportnummer, Zielportnummer) verwendet, um die Daten an den entsprechenden Prozess weiterzuleiten.
Nachdem Sie jetzt wissen, wie die Transportschicht Netzwerkanwendungen multiplexen und demultiplexen kann, fahren wir mit der Beschreibung des InternetTransportprotokolls UDP fort. Der nächste Abschnitt wird zeigen, dass UDP das Protokoll der Vermittlungsschicht um kaum mehr als einen Multiplex/DemultiplexDienst erweitert.
3.3 Verbindungsloser Transport: UDP
In diesem Abschnitt befassen wir uns mit den Merkmalen und der Funktionsweise
von UDP. Wir empfehlen dem Leser, sich noch einmal Abschnitt 2.1 anzusehen, der
eine Übersicht über das UDP-Dienstmodell beinhaltet, und Abschnitt 2.7, in dem
Socket-Programmierung über UDP beschrieben wird.
Als Motivation für diese Beschreibung von UDP nehmen wir an, Sie sind daran
interessiert, ein einfaches Transportprotokoll zu entwikkeln. Wie können Sie vorgehen? Sie können sich zuerst die Verwendung eines hirnlosen Transportprotokolls
überlegen. Insbesondere könnten Sie für die Sendeseite in Betracht ziehen, die Nachrichten von dem Anwendungsprozess entgegenzunehmen und sie direkt an die Vermittlungsschicht weiterzugeben. Auf der Empfangsseite können die von der Vermittlungsschicht ankommenden Nachrichten direkt an den Anwendungsprozess
weitergegeben werden. Wie wir im vorherigen Abschnitt aber gelernt haben, müssen
wir ein bisschen mehr als nichts tun. Zumindest muss die Transportschicht einen
Multiplex/Demultiplex-Dienst bereitstellen, damit Daten zwischen der Vermittlungsschicht und dem richtigen Prozess weitergeleitet werden können.
Das in RFC 768 definierte UDP tut so wenig, wie man sich für ein Transportprotokoll nur vorstellen kann. Abgesehen von der Multiplex/Demultiplex-Funktion und
einer geringen Fehlerprüfung fügt es nichts zu IP hinzu. Wenn sich der Anwendungsentwickler für UDP statt TCP entscheidet, spricht die Anwendung tatsächlich
fast direkt mit IP. UDP nimmt Nachrichten vom Anwendungsprozess entgegen,
hängt die Felder der Quell- und Zielportnummern für den Multiplex/DemultiplexDienst und einige kleinere Felder an und leitet das daraus resultierende Segment an
die Vermittlungsschicht weiter. Die Vermittlungsschicht verkapselt das Segment in
einem IP-Datagramm und macht dann einen Best-Effort-Versuch, um das Segment an
den empfangenden Host zu übertragen. Wenn das Segment beim empfangenden
Host ankommt, benutzt UDP die Zielportnummer, um die Daten des Segments an
den richtigen Anwendungsprozess zu übertragen. Man beachte, dass es bei UDP kein
Handshake zwischen den Einheiten der sendenden und empfangenden Transportschicht gibt, bevor ein Segment gesendet wird. Aus diesem Grund gilt UDP als verbindungslos.
3.3 Verbindungsloser Transport: UDP
DNS ist ein Beispiel für ein Protokoll der Anwendungsschicht, das UDP nutzt. Wenn
die DNS-Anwendung auf einem Host eine Anfrage stellen will, konstruiert sie eine
DNS-Anfragenachricht und leitet die Nachricht an ein UDP-Socket (siehe Abschnitt
2.7) weiter. Ohne ein Handshake durchzuführen, hängt UDP Header-Felder an die
Nachricht an und leitet das resultierende Segment an die Vermittlungsschicht weiter.
Die Vermittlungsschicht verkapselt das UDP-Segment in einem Datagramm und sendet das Datagramm an einen Name-Server. Die DNS-Anwendung auf dem anfragenden Host wartet dann auf eine Antwort. Erhält sie keine Antwort auf ihre Anfrage
(möglicherweise, weil das zugrunde liegende Netzwerk die Anfrage oder die Antwort verloren hat), versucht sie entweder, die Anfrage an einen anderen Name-Server
zu senden, oder sie informiert die anfragende Anwendung, dass sie keine Antwort
bekommen kann. Man beachte, dass DNS laut DNS-Spezifikation über TCP statt
UDP laufen kann; in der Praxis läuft DNS aber fast immer über UDP.
Sie wundern sich jetzt vielleicht, warum ein Anwendungsentwickler überhaupt
eine Anwendung über UDP statt über TCP aufbauen mag. Sollte man nicht TCP
immer den Vorzug vor UDP geben, weil TCP im Gegensatz zu UDP einen zuverlässigen Datentransferdienst bereitstellt? Die Antwort lautet »Nein«, weil sich UDP für
viele Anwendungen aus folgenden Gründen besser eignet:
õ Kein Verbindungsaufbau: Wie wir an späterer Stelle noch ausführen, nutzt TCP ein
Drei-Wege-Handshake, bevor es mit dem Datentransfer beginnt. UDP legt einfach
ohne jegliche Einleitungsformalitäten los. Folglich führt UDP keine Verzögerung
für den Aufbau einer Verbindung ein. Dies ist wahrscheinlich der Hauptgrund,
warum DNS über UDP und nicht über TCP läuft. DNS wäre über TCP viel langsamer. HTTP nutzt TCP statt UDP, weil Zuverlässigkeit für Web-Seiten mit Text
wichtig ist. Wie in Abschnitt 2.2 aber erwähnt wurde, ist ein Großteil des »World
Wide Wait« in HTTP der Verzögerung zuzuschreiben, die TCP durch den Verbindungsaufbau einführt.
õ Kein Verbindungszustand: TCP verwaltet in den Endsystemen einen Verbindungszustand. Dies beinhaltet Empfangs- und Sendepuffer, Parameter für die Überlastkontrolle sowie für die Sequenz- und Bestätigungsnummern. In Abschnitt 3.5
wird beschrieben, dass diese Zustandsinformationen notwendig sind, um den
zuverlässigen Datentransferdienst von TCP zu implementieren und Überlastkontrolle bereitzustellen. UDP verwaltet demgegenüber keinen Verbindungszustand
und keinen dieser Parameter. Aus diesem Grund kann ein Server, der sich einer
bestimmten Anwendung widmet, normalerweise viel mehr aktive Clients unterstützen, wenn die Anwendung über UDP und nicht über TCP läuft.
õ Geringer Overhead durch Paket-Header: Das TCP-Segment umfasst einen HeaderOverhead von 20 Byte im Gegensatz zu nur 8 Byte in UDP.
õ Unregulierte Senderate: TCP verfügt über einen Überlastkontrollmechanismus, der
den Sender drosselt, wenn eine oder mehrere Verbindungsleitungen zwischen
Sender und Empfänger überlastet werden. Dieses Drosseln kann schwerwiegende
Auswirkungen auf Echtzeitanwendungen haben, die einen gewissen Paketverlust
tolerieren können, aber eine minimale Senderate voraussetzen. Demgegenüber ist
die Geschwindigkeit, in der UDP Daten sendet, nur durch die Rate, in der die
Anwendung Daten erzeugt, die Fähigkeiten der Quelle (CPU, Taktrate usw.) und
die Zugangsbandbreite zum Internet beschränkt. Man beachte allerdings, dass der
empfangende Host nicht unbedingt alle Daten empfängt. Wenn das Netzwerk
überlastet ist, können einige Daten aufgrund eines Pufferüberlaufs im Router ver-
185
Kapitel 3 – Transportschicht
186
loren gehen. Folglich kann die Empfangsrate durch Netzwerküberlast begrenzt
werden, auch wenn die Senderate nicht beschränkt ist.
Abbildung 3.6 enthält eine Aufstellung beliebter Internet-Anwendungen und der
von ihnen genutzten Transportprotokolle. Wie erwartet, laufen E-Mail, RemoteLogin, das Web und Filetransfer über TCP. Alle diese Anwendungen benötigen den
zuverlässigen Datentransferdienst von TCP. Dennoch laufen viele wichtige Anwendungen über UDP und nicht über TCP. UDP wird für die Aktualisierung von RIPRouting-Tabellen (siehe Kapitel 4) benutzt, weil die Aktualisierungen periodisch
(normalerweise alle fünf Minuten) gesendet werden, so dass verlorene Aktualisierungen durch neuere ersetzt werden. UDP kommt auch für das Netzwerkmanagement
über SNMP (siehe Kapitel 8) zum Einsatz. UDP wird in diesem Fall gegenüber TCP
bevorzugt, weil Netzwerkmanagementanwendungen oft laufen, wenn sich das Netzwerk in einem Stresszustand befindet, nämlich wenn zuverlässiger Datentransfer mit
Überlastkontrolle kaum oder überhaupt nicht mehr möglich ist. Wie an früherer
Stelle erwähnt, läuft auch DNS über UDP, um die durch den Verbindungsaufbau von
TCP entstehenden Verzögerungen zu vermeiden.
Abbildung 3.6 Beliebte Internet-Anwendungen mit dem jeweils zugrunde liegenden
Transportprotokoll
Anwendung
Protokoll der
Anwendungsschicht
Zugrunde liegendes
Transportprotokoll
E-Mail
SMTP
TCP
Remote-Login
Telnet
TCP
Web
HTTP
TCP
Filetransfer
FTP
TCP
Remote File Server
NFS
überwiegend UDP
Streaming Multimedia
proprietär
überwiegend UDP
Internet-Telefonie
proprietär
überwiegend UDP
Netzwerkmanagement
SNMP
überwiegend UDP
Routing-Protokoll
RIP
überwiegend UDP
Namensauflösung
DNS
überwiegend UDP
Wir sehen in Abbildung 3.6, dass UDP heute auch vorwiegend für MultimediaAnwendungen, z. B. Internet-Phone, Echtzeit-Videokonferenzen und Audio-/VideoStreaming benutzt wird. Diese Anwendungen werden ausführlich in Kapitel 6
beschrieben. Vorläufig genügt es zu wissen, dass alle diese Anwendungen einen
gewissen Umfang an Paketverlust tolerieren können, so dass zuverlässiger Datentransfer für den Erfolg der Anwendung nicht unbedingt entscheidend ist. Außerdem
reagieren Echtzeitanwendungen wie Internet-Phone und Videokonferenzen sehr
schlecht auf die Überlastkontrolle von TCP. Aus diesen Gründen wählen Entwickler
von Multimedia-Anwendungen oft UDP statt TCP. Schließlich laufen auch MulticastAnwendungen über UDP, weil TCP mit Multicast nicht benutzt werden kann.
3.3 Verbindungsloser Transport: UDP
Obwohl es heute üblich ist, Multimedia-Anwendungen über UDP auszuführen, wird
dies doch auch recht kontrovers diskutiert, um es einmal vorsichtig auszudrücken.
Wie weiter oben erwähnt wurde, hat UDP keine Überlastkontrolle. Überlastkontrolle
ist aber notwendig, um zu verhindern, dass das Netzwerk in einen Zustand gerät, in
dem kaum mehr nützliche Arbeit möglich ist. Wenn jeder mit dem Streaming von
Video mit hoher Bitrate beginnen würde, ohne dass eine Überlastkontrolle angewandt wird, würde derart viel Paketüberlauf in den Routern entstehen, dass keiner
mehr etwas zu sehen bekäme. Folglich ist der Mangel an Überlastkontrolle in UDP
ein potenziell schwerwiegendes Problem [Floyd 1999]. Viele Wissenschaftler schlugen neue Mechanismen vor, um alle Quellen – auch UDP-Quellen – zur Durchführung einer adaptiven Überlastkontrolle zu zwingen [Mahdavi 1997; Floyd 2000].
Bevor wir die UDP-Segmentstruktur beschreiben, möchten wir darauf hinweisen,
dass es für eine Anwendung auch unter UDP möglich ist, einen zuverlässigen Datentransfer zu erhalten. Dies lässt sich bewerkstellig, indem die Anwendung selbst mit
Zuverlässigkeit ausgestattet wird (z. B. durch Hinzufügen von Bestätigungs- und
Neuübertragungsmechanismen wie diejenigen, die im nächsten Abschnitt beschrieben werden). Dies ist allerdings kein leichtes Unterfangen und kann einen Anwendungsentwickler viele Stunden an Debugging kosten. Dennoch ermöglicht es die Einbeziehung von Zuverlässigkeit direkt in die Anwendung, dass diese ebenfalls »ihr
Stückchen Kuchen erhält und essen kann«. Das heißt, Anwendungsprozesse können
zuverlässig miteinander kommunizieren, ohne sich den Einschränkungen hinsichtlich der Übertragungsrate, die vom TCP-Überlastkontrollmechanismus auferlegt
werden, unterwerfen zu müssen. Viele der heutigen proprietären Streaming-Anwendungen tun genau dies – sie laufen über UDP, haben aber eingebaute Bestätigungsund Neuübertragungsfunktionen, um Paketverlust zu reduzieren (siehe z. B. [Rhee
1998]).
3.3.1 UDP-Segmentstruktur
Die UDP-Segmentstruktur (siehe Abbildung 3.7) ist in RFC 768 definiert. Die Anwendungsdaten belegen das Datenfeld des UDP-Datagramms. Für DNS enthält das
Datenfeld z. B. entweder eine Anfrage- oder eine Antwortnachricht. Für eine Streaming-Audioanwendung ist das Datenfeld mit Audio-Samples gefüllt. Der UDP-Header hat nur vier aus je zwei Byte bestehende Felder. Wie im vorherigen Abschnitt
erwähnt, kann der Zielhost anhand der Portnummern die Anwendungsdaten an den
richtigen Prozess auf dem Zielhost weiterleiten (das ist die Demultiplexfunktion). Die
Prüfsumme wird vom empfangenden Host benutzt, um zu prüfen, ob Fehler in das
Segment eingeführt wurden. In Wahrheit wird die Prüfsumme auch über mehrere
Felder des IP-Headers und nicht nur über das UDP-Segment berechnet. Wir ignorieren dieses Detail aber, um den Wald vor lauter Bäumen nicht aus den Augen zu verlieren. Die Prüfsummenberechnung ist Inhalt des nächsten Abschnitts, während
grundlegende Prinzipien der Fehlererkennung in Abschnitt 5.1 beschrieben werden.
Das Längenfeld (Length) spezifiziert die Länge des UDP-Segments, einschließlich des
Headers, in Byte.
187
Kapitel 3 – Transportschicht
188
Abbildung 3.7 Die UDP-Segmentstruktur
32 Bit
Portnummer Quelle Portnummer Ziel
Länge
Prüfsumme
Anwendungsdaten
(Nachricht)
3.3.2 UDP-Prüfsumme
Die UDP-Prüfsumme dient der Fehlererkennung. Auf der Senderseite führt UDP das
Einer-Komplement der Summe aller 16-Bit-Wörter im Segment durch. Dieses Ergebnis wird in das Prüfsummenfeld (Checksum) des UDP-Segments eingefügt. Wir
geben hier ein einfaches Beispiel der Prüfsummenberechnung. Einzelheiten über die
effiziente Implementierung der Berechnung findet der Leser in RFC 1071 und die
Leistung bei Versendung echter Daten in [Stone 1998 und Stone 2000]. Als Beispiel
gehen wir von den folgenden drei 16-Bit-Wörtern aus:
0110011001100110
0101010101010101
0000111100001111
Die Summe der ersten dieser 16-Bit-Wörter ist
0110011001100110
0101010101010101
1011101110111011
Wenn wir das dritte Wort zur obigen Summe addieren, erhalten wir
1011101110111011
0000111100001111
1100101011001010
Das Einer-Komplement wird ermittelt, indem man alle Nullen in Einsen und alle Einsen in Nullen konvertiert. Das Einer-Komplement der Summe 1100101011001010 ist
somit 0011010100110101, was die Prüfsumme wird. Beim Empfänger werden alle vier
16-Bit-Wörter, einschließlich der Prüfsumme, addiert. Wenn sich keine Fehler in das
Paket einschleichen, lautet die Summe beim Empfänger 1111111111111111. Ist eines
der Bits eine Null, wissen wir, dass Fehler in das Paket eingeführt wurden.
Sie wundern sich vielleicht, warum UDP überhaupt eine Prüfsumme bereitstellt,
da viele Protokolle auf der Sicherungsschicht (darunter das beliebte Ethernet-Protokoll) ebenfalls Fehlerprüfung bieten. Der Grund ist, dass es keine Zusicherung gibt,
3.4 Prinzipien des zuverlässigen Datentransfers
dass alle Verbindungsleitungen zwischen der Quelle und dem Ziel Fehlerprüfung
bieten; eine Verbindungsleitung nutzt vielleicht ein Protokoll, das keine Fehlerprüfung bereitstellt. Da IP ja über praktisch jedes Schicht-2-Protokoll laufen soll, ist es
sinnvoll, Fehlerprüfung als Sicherheitsmaßnahme auf der Transportschicht bereitzustellen. Obwohl UDP Fehlerprüfung bietet, unternimmt es nichts, um den Fehler zu
beheben. Einige Implementierungen von UDP verwerfen das beschädigte Segment
einfach, während andere es mit einer Warnung an die Anwendung weitergeben.
Damit beenden wir unsere Beschreibung von UDP. Sie werden bald sehen, dass
TCP seinen Anwendungen zuverlässigen Datentransfer und weitere Dienste bietet,
die UDP nicht bietet. Natürlich ist TCP auch komplexer als UDP. Bevor wir mit der
Beschreibung von TCP beginnen, ist es an dieser Stelle hilfreich, einen Schritt zurück
zu machen und zuerst die grundlegenden Prinzipien von zuverlässigem Datentransfer zu beschreiben (siehe nächsten Abschnitt). In Abschnitt 3.5 beginnen wir mit den
Grundlagen von TCP, die auf diesen Prinzipien basieren.
3.4 Prinzipien des zuverlässigen Datentransfers
Dieser Abschnitt befasst sich mit dem zuverlässigen Datentransfer im Allgemeinen.
Dies ist sinnvoll, weil das Problem der Implementierung eines zuverlässigen Datentransfers nicht nur auf der Transportschicht, sondern auch auf der Sicherungs- und
Anwendungsschicht gelöst werden muss. Das allgemeine Problem ist folglich für
Netzwerke von zentraler Bedeutung. Auf einer »Hitliste« der zehn wichtigsten Probleme im gesamten Vernetzungsbereich wäre dies einer der obersten Kandidaten. Im
nächsten Abschnitt beschreiben wir TCP und zeigen insbesondere auf, dass TCP viele
der hier beschriebenen Prinzipien nutzt.
Abbildung 3.8 zeigt das Rahmenwerk für unsere Untersuchung des zuverlässigen
Datentransfers. Die Dienstabstraktion, die den Einheiten der höheren Schichten
bereitgestellt wird, ist ein zuverlässiger Kanal, durch den Daten übertragen werden
können. Auf einem zuverlässigen Kanal werden keine Bits verstümmelt (von 0 auf 1,
oder umgekehrt, umgedreht) oder verloren und alle kommen in der Reihenfolge an,
in der sie gesendet wurden. Dies entspricht genau dem Dienstmodell, das TCP den
nutzenden Internet-Anwendungen bietet.
Es gehört zum Aufgabenbereich eines zuverlässigen Datentransferprotokolls,
diese Dienstabstraktion zu implementieren. Diese Aufgabe wird durch die Tatsache
erschwert, dass die Schicht unter dem zuverlässigen Datentransferprotokoll unzuverlässig sein kann. Beispielsweise ist TCP ein zuverlässiges Datentransferprotokoll, das
auf einer unzuverlässigen (IP) Ende-zu-Ende-Netzwerkschicht aufsetzt. Allgemeiner
ausgedrückt, kann die Schicht unter den beiden zuverlässig kommunizierenden Endpunkten aus einer einzigen physikalischen Verbindung (z. B. wie im Fall eines Datentransferprotokolls auf der Sicherungsschicht) oder aus einem globalen Internetwork
(z. B. wie im Fall eines Protokolls der Transportschicht) bestehen. Für unsere Zwecke
genügt es aber, diese untere Schicht einfach als unzuverlässigen Punkt-zu-PunktKanal zu betrachten.
In diesem Abschnitt entwickeln wir stufenweise die Sender- und Empfängerseite
eines zuverlässigen Datentransferprotokolls, wobei wir zunehmend komplexere
Modelle des zugrunde liegenden Kanals betrachten. Abbildung 3.8 (b) zeigt die
Schnittstellen des Datentransferprotokolls. Die Sendeseite des Datentransferprotokolls wird von oben durch einen Aufruf von rdt_send() aufgerufen. Dadurch werden die zu übertragenden Daten an die obere Schicht auf der Empfangsseite weiter-
189
Kapitel 3 – Transportschicht
190
Anwendungsschicht
Abbildung 3.8 Dienstmodell und -implementierung des zuverlässigen Datentransfers
Sendeprozess
Daten
Empfangsprozess
Daten
rdt_send()
Transportschicht
Zuverlässiger Kanal
Daten
Zuverlässiges
Datentransferprot.
(Sendeseite)
udt_send()
Paket
Daten
deliver data()
Zuverlässiges
Datentransferprot.
(Empfangsseite)
Paket
rdt_rcv()
Unzuverlässiger Kanal
(a) Bereitgestellter Dienst
(b) Dienstimplementierung
gegeben. (Hier steht rdt für »zuverlässiges Datentransferprotokoll« und _send für die
Sendeseite, die rdt aufruft. Der erste Schritt in der Entwicklung eines jeden Protokolls ist die Auswahl eines guten Namens!) Auf der Empfangsseite wird rdt_rcv()
aufgerufen, wenn ein Paket von der empfangenden Seite des Kanals ankommt. Wenn
das rdt-Protokoll Daten an die obere Schicht übertragen will, bewirkt es dies durch
Aufruf von deliver_data(). Im Folgenden verwenden wir für die PDU den Begriff
»Paket« statt »Segment«. Da die in diesem Abschnitt behandelte Theorie auf Computernetzwerke im Allgemeinen und nicht nur auf die Internet-Transportschicht
zutrifft, halten wir den generischen Begriff »Paket« für besser geeignet.
In diesem Abschnitt betrachten wir nur den Fall eines unidirektionalen Datentransfers, d. h. Datentransfer von der sendenden zur empfangenden Seite. Der Fall
eines zuverlässigen bidirektionalen Datentransfers (d. h. Vollduplex) ist konzeptionell nicht schwieriger, aber viel umständlicher zu beschreiben. Obwohl wir uns nur
mit dem unidirektionalen Datentransfer befassen, ist zu beachten, dass die sendende
und die empfangende Seite des Protokolls dennoch Pakete in beiden Richtungen übertragen müssen, wie aus Abbildung 3.8 ersichtlich wird. Wir werden in Kürze sehen,
dass zusätzlich zum Austausch von Paketen, die die zu übertragenden Daten enthalten, die sendende und empfangende Seite von rdt auch Steuerpakete in beide Richtungen austauschen müssen. Beide Seiten – Sender und Empfänger von rdt – senden
Pakete an die jeweils andere Seite durch einen Aufruf von udt_send() (wobei udt für
»unzuverlässiger Datentransfer« steht).
3.4 Prinzipien des zuverlässigen Datentransfers
3.4.1 Aufbau eines zuverlässigen Datentransferprotokolls
Wir gehen im Folgenden eine Reihe von Protokollen durch, die schrittweise komplexer werden, und schließen mit der Beschreibung eines einwandfreien zuverlässigen
Datentransferprotokolls.
Zuverlässiger Datentransfer über einen absolut zuverlässigen Kanal:
rdt1.0
Wir betrachten zuerst den einfachsten Fall, bei dem der zugrunde liegende Kanal
absolut zuverlässig ist. Das Protokoll, das wir rdt1.0 nennen, ist sehr einfach. Die
FSM-Definitionen (Finite-State Machine) für Sender und Empfänger von rdt1.0
sind in Abbildung 3.9 dargestellt. Die Sender- und Empfänger-FSMs in Abbildung 3.9
haben jeweils nur einen Zustand. Die Pfeile in der FSM-Beschreibung bezeichnen den
Übergang des Protokolls von einem Zustand in einen anderen. (Da jede FSM in
Abbildung 3.9 nur einen Zustand hat, erfolgt ein Übergang natürlich von einem
Zustand zurück in denselben; im weiteren Verlauf verwenden wir komplexere
Zustandsdiagramme.) Das Ereignis, das den Übergang auslöst, ist oberhalb der horizontalen Linie dargestellt, die den Übergang beschriftet, und die Aktion(en), die
unternommen werden, wenn das Ereignis eintritt, stehen unter der horizontalen
Linie.
Abbildung 3.9 rdt1.0 – ein Protokoll für einen zuverlässigen Kanal
Warte auf
Aufruf
von oben
rdt_send(data)
make_pkt(packet,
data)
udt_send(packet)
(a) rdt1.0: Sendeseite
Warte auf
Aufruf
von unten
rdt_rcv(packet)
extract(packet,data)
deliver_data(data)
(b) rdt1.0: Empfangsseite
Die Sendeseite von rdt nimmt einfach Daten von der oberen Schicht über das Ereignis rdt_send(data) an, gibt die Daten (über die Aktion make_pkt packet,data)) in ein
Paket und schickt das Paket in den Kanal. In der Praxis würde das Ereignis
rdt_send(data) aus einem Prozeduraufruf (z. B. von rdt_send()) durch die höherschichtige Anwendung resultieren.
Auf der Empfangsseite empfängt rdt über das Ereignis rdt_rcv(packet) ein
Paket vom zugrunde liegenden Kanal, nimmt die Daten (über die Aktion
extract(packet,data)) aus dem Paket und gibt sie an die obere Schicht weiter. In der
Praxis würde das Ereignis rdt_rcv(packet) aus einem Prozeduraufruf (z. B. von
rdt_rcv()) durch das niederschichtige Protokoll resultieren.
Bei diesem einfachen Protokoll besteht kein Unterschied zwischen einer Datenund einer Paketeinheit. Außerdem fließen alle Pakete vom Sender zum Empfänger.
Bei einem vollkommen zuverlässigen Kanal besteht auf der Empfängerseite keine
Notwendigkeit, irgendeine Rückmeldung (Feedback) an den Sender zu schicken, da
nichts schief gehen kann! Darüber hinaus sind wir davon ausgegangen, dass der
Empfänger in der Lage ist, die Daten so schnell zu empfangen wie sie der Sender sen-
191
192
Kapitel 3 – Transportschicht
det. Folglich besteht für den Empfänger keine Notwendigkeit, den Sender zum »Verlangsamen« aufzufordern.
Zuverlässiger Datentransfer über einen Kanal mit Bitfehlern: rdt2.0
Bei einem realistischeren Modell des zugrunde liegenden Kanals können Bits in
einem Paket beschädigt werden. Solche Bitfehler entstehen normalerweise in den
physikalischen Komponenten eines Netzwerks, während ein Paket übertragen oder
zwischengespeichert wird. Wir wollen vorläufig wieder davon ausgehen, dass alle
übertragenen Pakete in der richtigen Reihenfolge ankommen (obwohl einige Bits
beschädigt sein können).
Bevor wir ein Protokoll für die zuverlässige Kommunikation über einen solchen
Kanal entwickeln, untersuchen wir zuerst, wie sich Menschen in einer solchen Situation verhalten würden. Angenommen, Sie diktieren eine lange Nachricht am Telefon.
In einem typischen Szenario sagt die Person am anderen Ende vielleicht nach jedem
Satz, den sie gehört, verstanden und aufgezeichnet hat, »OK«. Wenn die andere Person einen Satz nicht richtig versteht, werden Sie gebeten, ihn zu wiederholen. Dieses
Nachrichtendiktierprotokoll benutzt sowohl positive Bestätigungen (»OK«) als auch
negative Bestätigungen (»Bitte wiederholen Sie das.«) Diese Steuernachrichten
ermöglichen es dem Empfänger, den Sender darüber zu informieren, was korrekt und
was fehlerhaft empfangen wurde. In einem Computernetzwerk basieren zuverlässige
Datentransferprotokolle auf solchen Neuübertragungen, die man als ARQ-Protokolle (Automatic Repeat reQuest) bezeichnet.
Grundsätzlich sind in ARQ-Protokollen drei weitere Protokollfähigkeiten erforderlich, um vorhandene Bitfehler zu behandeln:
õ Fehlererkennung: Zunächst bedarf es eines Mechanismus, damit der Empfänger
Bitfehler erkennen kann. Wir wissen aus dem vorherigen Abschnitt, dass UDP das
Internet-Prüfsummenfeld genau für diesen Zweck benutzt. In Kapitel 5 werden
Fehlererkennungs- und -korrekturtechniken ausführlicher beschrieben. Diese
Techniken ermöglichen es dem Empfänger, Paketbitfehler zu erkennen und möglicherweise zu korrigieren. Vorläufig genügt es zu wissen, dass für diese Techniken zusätzliche Bits (abgesehen von den zu übertragenden Originaldatenbits)
vom Sender zum Empfänger gesendet werden müssen. Diese Bits werden im
Paketprüfsummenfeld des rdt2.0-Datenpakets erfasst.
õ Empfänger-Feedback: Da Sender und Empfänger normalerweise auf unterschiedlichen Endsystemen ausgeführt werden, die möglicherweise Tausende von Kilometern auseinander liegen, erhält der Sender über die Lage auf der Empfängerseite
(in diesem Fall, ob ein Paket korrekt empfangen wurde) nur dadurch Informationen, dass der Empfänger dem Sender ausdrücklich Rückmeldung macht. Die
positive (ACK) und negative (NAK) Bestätigung in dem Nachrichtendiktierszenario sind Beispiele eines solchen Feedbacks. Das rdt2.0-Protokoll wird ebenfalls
ACK- und NAK-Pakete vom Empfänger zum Sender zurücksenden. Im Prinzip
brauchen diese Pakete nur ein Bit lang zu sein; beispielsweise könnte ein 0-Wert
ein NAK und ein 1-Wert ein ACK bedeuten.
õ Neuübertragung: Ein Paket, das fehlerhaft beim Empfänger ankommt, wird vom
Sender erneut übertragen.
Abbildung 3.10 zeigt die FSM-Darstellung von rdt2.0, einem Datentransferprotokoll
mit Fehlererkennung sowie positiven und negativen Bestätigungen.
3.4 Prinzipien des zuverlässigen Datentransfers
Abbildung 3.10 rdt2.0 – ein Protokoll für einen Kanal mit Bitfehlern
rdt_send(data)
compute checksum
make_pkt(sndpkt, data, checksum)
udt_send(sndpkt)
Warte auf
Aufruf von
oben
Warte auf
ACK oder
NAK
rdt_rcv(rcvpkt)
&& isNAK(rcvpkt)
udt_send(sndpkt)
rdt_rcv(rcvpkt) && isACK(rcvpkt)
(a) rdt2.0: Sendeseite
rdt_rcv(rcvpkt) && corrupt(rcvpkt)
udt_send(NAK)
Warte auf
Aufruf von
unten
rdt_rcv(rcvpkt) && notcorrupt(rcvpkt)
extract(rcvpkt,data)
deliver_data(data)
udt_send(ACK)
(b) rdt2.0: Empfangsseite
Die Sendeseite von rdt2.0 hat zwei Zustände. In einem Zustand wartet das Protokoll auf der Sendeseite auf Daten, die von der höheren Schicht nach unten weitergereicht werden. Im zweiten Zustand wartet das Senderprotokoll auf ein ACK- oder
NAK-Paket vom Empfänger. Wenn ein ACK-Paket ankommt (rdt_rcv(rcvpkt) &&
isACK (rcvpkt) in Abbildung 3.10 entspricht diesem Ereignis), weiß der Sender,
dass das zuletzt übertragene Paket korrekt empfangen wurde. Folglich kehrt das
Protokoll in den Zustand des Wartens auf Daten von der höheren Schicht zurück.
Kommt ein NAK an, überträgt das Protokoll das letzte Paket erneut und wartet auf
ein ACK oder NAK vom Empfänger als Reaktion auf das erneut übertragene Datenpaket. Wichtig ist hier, dass der Empfänger, wenn er sich im Wartezustand auf ein
ACK oder NAK befindet, keine weiteren Daten von der höheren Schicht empfangen
193
194
Kapitel 3 – Transportschicht
kann; dies ist erst wieder möglich, nachdem der Sender ein ACK empfangen hat und
diesen Zustand verlässt. Folglich überträgt der Sender so lange keine neuen Daten,
bis er sicher ist, dass der Empfänger das aktuelle Paket korrekt empfangen hat. Aufgrund dieses Verhaltens bezeichnet man Protokolle wie unser rdt2.0 als Stop-andWait-Protokolle.
Die empfängerseitige FSM für rdt2.0 hat wiederum nur einen einzigen Zustand.
Bei Ankunft eines Pakets antwortet der Empfänger entweder mit einem ACK oder
einem NAK, je nachdem, ob das empfangene Paket beschädigt ist. In Abbildung 3.10
entspricht die Notation rdt_rcv(rcvpkt) && corrupt(rcvpkt) dem Ereignis, bei dem
ein Paket fehlerhaft angekommen ist.
Das Protokoll rdt2.0 scheint vielleicht zu funktionieren; leider hat es aber einen
fatalen Fehler. Wir haben nicht die Möglichkeit berücksichtigt, dass auch das ACKoder NAK-Paket beschädigt werden kann! (Bevor wir fortfahren, sollten Sie sich
überlegen, wie man dieses Problem beheben kann.) Leider ist dies nicht so harmlos,
wie es vielleicht scheint. Als Minimum müssen wir die ACK/NAK-Pakete um Prüfsummenbits erweitern, um solche Fehler erkennen zu können. Die schwierigere
Frage betrifft die Wiederherstellung des Protokolls nach Fehlern in ACK- oder NAKPaketen. Die Schwierigkeit ist hier, dass der Sender, wenn ein ACK oder NAK
beschädigt ist, nicht wissen kann, ob der Empfänger die zuletzt übertragenen Daten
korrekt empfangen hat. Wir betrachten die folgenden drei Möglichkeiten für die
Behandlung beschädigter ACKs oder NAKs:
õ Für die erste Variante betrachte man, was ein Mensch in dem Nachrichtendiktierszenario tun würde. Hat der Sprecher die Antwort »OK« oder »Bitte wiederholen
Sie das« des Empfängers nicht verstanden, so fragt er vielleicht »Was haben Sie
gesagt?« (womit ein neuer Typ von Sender-an-Empfänger-Paket in unser Protokoll eingeführt wird). Der Empfänger würde dann die Antwort wiederholen. Was
aber, wenn das »Was haben Sie gesagt?« des Sprechers beschädigt wird? Da der
Empfänger keine Ahnung hat, ob der verstümmelte Satz Teil des Diktats oder
einer Bitte um Wiederholung der letzten Antwort ist, würde er wahrscheinlich mit
»Was haben Sie gesagt?« antworten. Dann könnte natürlich auch diese Antwort
beschädigt werden. Sie sehen, wir haben es hier mit einer kniffligen Angelegenheit zu tun.
õ Bei der zweiten Möglichkeit werden ausreichend Prüfsummenbits hinzugefügt,
damit der Sender Bitfehler nicht nur erkennen, sondern auch die Fehlersituation
beheben kann. Dies löst das unmittelbare Problem eines Kanals, der Pakete zwar
beschädigen, aber nicht verlieren kann.
õ Bei der dritten Möglichkeit überträgt der Sender das aktuelle Datenpaket einfach
noch einmal, wenn er ein beschädigtes ACK- oder NAK-Paket empfängt. Diese
Möglichkeit führt aber zu Duplikatpaketen auf dem Kanal vom Sender zum
Empfänger. Die grundlegende Schwierigkeit bei Duplikatpaketen ist, dass der
Empfänger nicht weiß, ob das zuletzt gesendete ACK oder NAK vom Sender korrekt empfangen wurde. Somit kann er grundsätzlich nicht wissen, ob ein ankommendes Paket neue Daten enthält oder eine Neuübertragung darstellt!
Als einfache Lösung für dieses neue Problem (die in fast allen existierenden Datentransferprotokollen, auch TCP, angewandt wird) können wir ein neues Feld zum
Datenpaket hinzufügen und den Sender seine Datenpakete nummerieren lassen.
Hierfür setzt er eine Sequenznummer in dieses neue Feld. Der Empfänger muss
3.4 Prinzipien des zuverlässigen Datentransfers
dann nur diese Sequenznummer ansehen, um festzustellen, ob es sich bei dem empfangenen Paket um eine Neuübertragung handelt. Für diesen einfachen Fall eines
Stop-and-Wait-Protokolls genügt eine 1-Bit-Sequenznummer, da der Empfänger
dadurch feststellen kann, ob der Sender das zuvor übertragene Paket (das empfangene Paket hat die gleiche Sequenznummer wie das zuletzt angekommene Paket)
oder ein neues Paket (die Sequenznummer ändert sich; sie wird in der Modulus-2Arithmetik »nach vorn« geschoben) sendet. Da wir im Moment davon ausgehen,
dass der Kanal keine Pakete verliert, müssen ACK- und NAK-Pakete selbst die
Sequenznummer des Pakets, das sie bestätigen, nicht angeben. Der Sender weiß, dass
ein empfangenes ACK- oder NAK-Paket (beschädigt oder nicht) als Reaktion auf sein
zuletzt übertragenes Datenpaket erzeugt wurde.
Die Abbildungen 3.11 und 3.12 zeigen die FSM-Beschreibung für rdt2.1, unsere
korrigierte Version von rdt2.0. In rdt2.1 haben die Sender- und Empfänger-FSM
jeweils doppelt so viele Zustände wie vorher. Der Grund dafür ist, dass der Protokollzustand nun widerspiegeln muss, ob das momentan (vom Sender) übertragene oder
(vom Empfänger) erwartete Paket die Folgenummer 0 oder 1 haben muss. Man
beachte, dass die Aktionen in den Zuständen, in denen ein mit 0 nummeriertes Paket
gesendet oder erwartet wird, Spiegelbilder derjenigen sind, in denen ein mit 1 nummeriertes Paket gesendet oder erwartet wird. Der einzige Unterschied ist die Behandlung der Sequenznummer.
Abbildung 3.11 Der Sender von rdt2.1
rdt_send(data)
compute chksum
make_pkt(sndpkt,0,data,chksum)
udt_send(sndpkt)
Warte auf
Aufruf 0
von oben
Warte auf
ACK oder
NAK 0
rdt_rcv(rcvpkt)
&& notcorrupt(rcvpkt)
&& isACK(rcvpkt)
Warte auf
ACK oder
NAK 1
rdt_rcv(rcvpkt) &&
( corrupt(rcvpkt) ||
isNAK(rcvpkt))
udt_send(sndpkt)
rdt_rcv(rcvpkt)
&& notcorrupt(rcvpkt)
&& isACK(rcvpkt)
Warte auf
Aufruf 1
von oben
rdt_rcv(rcvpkt) &&
(corrupt(rcvpkt)||
isNAK(rcvpkt))
rdt_send(data)
udt_send(sndpkt)
compute chksum
make_pkt(sndpkt,1,data,chksum)
udt_send(snkpkt)
195
Kapitel 3 – Transportschicht
196
Abbildung 3.12 Der Empfänger von rdt2.1
rdt_rcv(rcvpkt)
&& notcorrupt(rcvpkt)
&& has_seq0(rcvpkt)
rdt_rcv(rcvpkt)
&& corrupt(rcvpkt)
extract(rcvpkt,data)
deliver_data(data)
compute chksum
make_pkt(sendpkt,ACK,chksum)
udt_send(sndpkt)
rdt_rcv(rcvpkt)
&& corrupt(rcvpkt)
compute chksum
make_pkt(sndpkt,NAK,chksum)
udt_send(sndpkt)
Warte auf
0 von
unten
rdt_rcv(rcvpkt)
&& notcorrupt(rcvpkt)
&& has_seq1(rcvpkt)
compute chksum
make_pkt(sndpkt,NAK,chksum)
udt_send(sndpkt)
Warte auf
1 von
unten
rdt_rcv(rcvpkt)
&& notcorrupt(rcvpkt)
&& has_seq0(rcvpkt)
rdt_rcv(rcvpkt)
compute chksum
compute chksum
make_pkt(sndpkt,ACK, && notcorrupt(rcvpkt) make_pkt(sndpkt,ACK,
chksum)
&& has_seq1(rcvpkt)
chksum)
udt_send(sndpkt)
udt_send(sndpkt)
extract(rcvpkt,data)
deliver_data(data)
compute chksum
make_pkt(sendpkt,ACK,chksum)
udt_send(sndpkt)
Das Protokoll rdt2.1 benutzt positive (ACK) und negative (NAK) Bestätigungen
vom Empfänger zum Sender. Wenn ein Paket außer der Reihe ankommt, sendet der
Empfänger eine positive Bestätigung für das empfangene Paket. Wenn ein beschädigtes Paket ankommt, sendet der Empfänger eine negative Bestätigung. Wir können die
gleiche Wirkung wie ein NAK erzielen, wenn wir statt eines NAK für das zuletzt korrekt empfangene Paket ein ACK senden. Ein Sender, der zwei ACKs für das gleiche
Paket (d. h. Duplikat-ACKs) empfängt, weiß, dass der Empfänger das Paket nach
demjenigen, das zweimal bestätigt wird, nicht korrekt empfangen hat. Viele TCPImplementierungen nutzen den Empfang so genannter »dreifacher Duplikat-ACKs«
(drei ACK-Pakete, die alle das gleiche, bereits bestätigte Paket bestätigen), um den
Sender zu einer Neuübertragung zu veranlassen. Unser NAK-freies zuverlässiges
Datentransferprotokoll für einen Kanal mit Bitfehlern ist rdt2.2 (siehe Abbildungen
3.13 und 3.14).
Zuverlässiger Datentransfer über einen verlustbehafteten
Kanal mit Bitfehlern: rdt3.0
Wir nehmen jetzt an, dass zusätzlich zu beschädigten Bits im zugrunde liegenden
Kanal auch Pakete verloren gehen können, was in den heutigen Computernetzwerken
(auch im Internet) recht häufig vorkommt. Das Protokoll muss nun zwei zusätzliche
Fragen berücksichtigen: Wie ein Paketverlust erkannt wird und was unternommen
werden kann, falls es zu einem solchen Verlust kommt. Die Verwendung von Prüfsummen, Sequenznummern, ACK-Paketen und Neuübertragungen – die in rdt2.2 bereits
entwickelten Techniken – ermöglichen es uns, die zweite Frage zu klären. Die Behandlung des ersten Problembereichs setzt einen neuen Protokollmechanismus voraus.
3.4 Prinzipien des zuverlässigen Datentransfers
Abbildung 3.13 Der Sender von rdt2.2
rdt_send(data)
compute chksum
make_pkt(sndpkt,0,data,chksum)
udt_send(sndpkt)
rdt_rcv(rcvpkt)
&& notcorrupt(rcvpkt)
&& isACK(rcvpkt,1)
Warte auf
Aufruf von
oben
rdt_rcv(rcvpkt) &&
(corrupt(rcvpkt) ||
isACK(rcvpkt,0))
rdt_rcv(rcvpkt) &&
(corrupt(rcvpkt) ||
isACK(rcvpkt,1))
Warte auf
ACK0
Warte auf
Aufruf von
oben
Warte auf
ACK1
udt_send(sndpkt)
rdt_rcv(rcvpkt)
&& notcorrupt(rcvpkt)
&& isACK(rcvpkt,0)
udt_send(sndpkt)
rdt_send(data)
compute chksum
make_pkt(sndpkt,1,data,chksum)
udt_send(sndpkt)
Abbildung 3.14 Der Empfänger von rdt2.2
di
rdt_rcv(rcvpkt)
&& notcorrupt(rcvpkt)
&& has_seq0(recvpkt)
extract(rcvpkt,data)
deliver_data(data)
compute chksum
make_pkt(sendpkt,ACK0,chksum)
udt_send(sndpkt)
rdt_rcv(rcvpkt)
&& (corrupt(rcvpkt) ||
has_seq1(rcvpkt))
udt_send(sndpkt)
Warte auf
0 von
unten
Warte auf
1 von
unten
rdt_rcv(rcvpkt)
&& (corrupt(rcvpkt) ||
has_seq0(rcvpkt))
udt_send(sndpkt)
rdt_rcv(rcvpkt)
&& notcorrupt(rcvpkt)
&& has_seq1(recvpkt)
extract(rcvpkt,data)
deliver_data(data)
compute chksum
make_pkt(sendpkt,ACK1,chksum)
udt_send(sndpkt)
197
198
Kapitel 3 – Transportschicht
Für die Behandlung von Paketverlusten existieren viele mögliche Ansätze (mehrere
weitere werden in den Übungen am Ende dieses Kapitels behandelt). Hier belasten
wir den Sender mit der Aufgabe der Erkennung von verlorenen Paketen und der
Wiederherstellung nach einem solchen Ereignis. Angenommen, der Sender überträgt
ein Datenpaket und entweder dieses Paket oder das ACK des Empfängers für dieses
Paket geht verloren. In beiden Fällen erhält der Sender keine Antwort vom Empfänger. Ist der Sender bereit, so lange zu warten, bis er sicher sein kann, dass das Paket
verloren gegangen ist, kann er anschließend das Datenpaket einfach erneut übertragen. Sie sollten sich selbst davon überzeugen, dass dieses Protokoll tatsächlich funktioniert.
Doch wie lange muss der Sender warten, um sicher zu sein, dass etwas verloren
gegangen ist? Natürlich muss er mindestens so lange warten, bis eine Roundtrip-Verzögerung zwischen Sender und Empfänger verstrichen ist (was die Zwischenspeicherung in Routern oder Gateways auf der Strecke beinhalten kann), zuzüglich der für
die Verarbeitung eines Pakets beim Empfänger erforderlichen Zeit. In vielen Netzwerken lässt sich diese maximale Worst-Case-Verzögerung schwer schätzen, ganz zu
schweigen von Gewissheit. Außerdem sollte das Protokoll einen Paketverlust so
schnell wie möglich beheben. Das Warten auf eine Worst-Case-Verzögerung könnte
eine lange Zeit bedeuten, bis ein Fehler-Recovery eingeleitet wird. Deshalb wird in
der Praxis meist der Ansatz angewandt, dass der Sender einen Zeitwert »mit
Bedacht« auswählt, ob ein Paketverlust wahrscheinlich, aber nicht mit Sicherheit eingetreten ist. Wenn innerhalb dieser Zeit kein ACK ankommt, wird das Paket erneut
übertragen. Wenn sich bei einem Paket eine besonders große Verzögerung ergibt,
kann der Sender das Paket erneut übertragen, auch wenn weder das Datenpaket
noch sein ACK verloren gegangen ist. Dies führt die Möglichkeit von Duplikatpaketen in dem Kanal zwischen Sender und Empfänger ein. Zum Glück verfügt das Protokoll rdt2.2 bereits über ausreichend Funktionalität (d. h. Sequenznummern), um
den Fall von Duplikatpaketen behandeln zu können.
Aus Sicht des Senders ist die Neuübertragung ein Allheilmittel. Der Sender weiß
nicht, ob ein Datenpaket oder ein ACK verloren gegangen ist oder sich einfach übermäßig verzögert hat. In allen Fällen erfolgt die gleiche Aktion: Neuübertragung. Um
einen auf Zeit basierten Neuübertragungsmechanismus zu implementieren, ist ein
Countdown-Timer erforderlich, der den Sender nach Ablauf der Frist unterbrechen
kann. Der Sender muss folglich in der Lage sein, (1) den Timer jedes Mal, wenn ein
Paket (erstmalig oder erneut übertragenes Paket) gesendet wird, zu starten, (2) auf
ein Timer-Interrupt (durch Einleitung entsprechender Aktionen) zu reagieren und (3)
den Timer zu stoppen.
Der Verlust von sendererzeugten Duplikatpaketen und Daten- oder ACK-Paketen
macht auch die Verarbeitung der vom Sender empfangenen ACK-Pakete komplizierter. Wie kann der Sender bei Ankunft eines ACK wissen, ob es vom Empfänger als
Reaktion auf sein eigenes zuletzt übertragenes Paket gesendet wurde oder ob es sich
um ein verzögertes ACK handelt, das als Reaktion auf eine frühere Übertragung
eines anderen Datenpakets gesendet wurde? Als Ausweg aus diesem Dilemma wird
das ACK-Paket um ein Bestätigungsfeld (ACK-Feld) erweitert. Wenn der Empfänger
ein ACK erzeugt, kopiert er die Sequenznummer des zu bestätigenden Datenpakets
in dieses Bestätigungsfeld. Durch Prüfung des Inhalts dieses Bestätigungsfelds kann
der Sender die Sequenznummer des Pakets ermitteln, das tatsächlich bestätigt wird.
Abbildung 3.15 zeigt die Sender-FSM für rdt3.0. Dieses Protokoll überträgt Daten
zuverlässig auf einem Kanal, der potenziell Pakete beschädigen oder verlieren kann.
3.4 Prinzipien des zuverlässigen Datentransfers
Abbildung 3.16 zeigt, wie das Protokoll arbeitet, wenn keine Pakete verloren gegangen sind oder verzögert wurden, und wie verlorene Datenpakete behandelt werden.
In Abbildung 3.16 erfolgt der Zeitverlauf von oben nach unten im Diagramm. Man
beachte, dass die Empfangszeit eines Pakets aufgrund von Übertragungs- und Ausbreitungsverzögerungen notwendigerweise später als die Sendezeit für ein Paket ist.
In Abbildung 3.16 (a) bis (d) bedeuten die Klammern auf der Sendeseite die Zeiten,
zu denen ein Timer gesetzt wird und später abläuft. In den Übungen am Ende dieses
Kapitels werden mehrere weitere Aspekte dieses Protokolls behandelt. Da die
Sequenznummern von Paketen zwischen 0 und 1 wechseln, wird ein Protokoll wie
rdt3.0 auch als Alternating-Bit-Protokoll bezeichnet.
Abbildung 3.15 Der Sender von rdt3.0
rdt_send(data)
compute chksum
make_pkt(sndpkt,0,data,chksum)
udt_send(sndpkt)
start_timer
rdt_rcv(rcvpkt) &&
(corrupt(rcvpkt) ||
isACK(rcvpkt,1))
rdt_rcv(rcvpkt)
Warte auf
Aufruf von
oben 0
Warte auf
ACK0
timeout
udt_send(sndpkt)
start_timer
rdt_rcv(rcvpkt)
&& notcorrupt(rcvpkt)
&& isACK(rcvpkt,1)
timeout
udt_send(sndpkt)
start_timer
rdt_rcv(rcvpkt) &&
(corrupt(rcvpkt) ||
isACK(rcvpkt,0))
Warte auf
ACK1
rdt_rcv(rcvpkt)
&& notcorrupt(rcvpkt)
&& isACK(rcvpkt,0)
Warte auf
Aufruf von
oben 1
rdt_rcv(rcvpkt)
rdt_send(data)
compute chksum
make_pkt(sndpkt,1,data,chksum)
udt_send(sndpkt)
start_timer
Die wichtigsten Elemente eines Datentransferprotokolls sind nun zusammengestellt.
Prüfsummen, Sequenznummern, Timer sowie positive und negative Bestätigungspakete spielen eine wichtige Rolle in der Operation des Protokolls. Wir haben jetzt ein
funktionierendes zuverlässiges Datentransferprotokoll!
199
Kapitel 3 – Transportschicht
200
Abbildung 3.16 Operation von rdt3.0, dem Alternating-Bit-Protokoll
Sender
Empfänger
send pkt0
rcv pkt0
send ACK0
ACK0
pkt1
ACK1
rcv ACK1
send pkt0
pkt0
ACK0
Empfänger
pkt0
send pkt0
pkt0
rcv ACK0
send pkt1
Sender
rcv pkt1
send ACK1
rcv ACK0
send pkt1
timeout
resend pkt1
rcv pkt0
send ACK0
rcv ACK1
send pkt0
rcv pkt0
send ACK0
ACK0
pkt1
X (verloren)
pkt1
rcv pkt1
send ACK1
ACK1
pkt0
rcv pkt0
send ACK0
ACK0
(b) Verlorenes Paket
(a) Betrieb ohne Verlust
Sender
Empfänger
send pkt0
pkt0
pkt1
ACK1
(verloren) X
pkt1
ACK1
rcv ACK1
send pkt0
pkt0
ACK0
Empfänger
pkt0
send pkt0
rcv pkt0
send ACK0
ACK0
rcv ACK0
send pkt1
timeout
resend pkt1
Sender
rcv pkt1
send ACK1
rcv pkt1
(detect
duplicate)
send ACK1
rcv pkt0
send ACK0
ACK0
rcv ACK0
send pkt1
timeout
resend pkt1
rcv ACK1
send pkt0
pkt1
rcv pkt1
send ACK1
pkt1 rcv pkt 1
(detect
ACK1
duplicate)
pkt0 send ACK1
rcv pkt0
ACK0
send ACK0
ACK1
rcv pkt0
send ACK0
(c) Verlorenes ACK
(d) Frühzeitiges Timeout
3.4.2 Zuverlässige Datentransferprotokolle mit Pipelining
Das Protokoll rdt3.0 ist funktionell ein korrektes Protokoll, dürfte aber hinsichtlich
seiner Leistung kaum jemanden zufrieden stellen, insbesondere in den heutigen
Hochgeschwindigkeitsnetzen. Das Leistungsproblem von rdt3.0 basiert vor allem
auf der Tatsache, dass es ein Stop-and-Wait-Protokoll ist.
Um die Leistungsauswirkung dieses Stop-and-Wait-Verhaltens richtig einschätzen
zu können, betrachten wir einen idealisierten Fall mit zwei Endhosts, von denen sich
einer an der Westküste und der andere an der Ostküste der USA befindet. Die Ausbreitungsverzögerung in Lichtgeschwindigkeit, Tprop, zwischen diesen beiden Endsystemen beträgt ungefähr 15 Millisekunden. Es wird angenommen, dass sie über
einen Kanal mit einer Kapazität C von 1 Gigabit (109 Bit) pro Sekunde verbunden
sind. Bei einer Paketgröße SP von 1 KByte pro Paket, einschließlich der Header-Felder und Daten, ergibt sich die erforderliche Zeit, um das Paket tatsächlich über die
1-Gbps-Verbindungsleitung zu übertragen, wie folgt:
3.4 Prinzipien des zuverlässigen Datentransfers
8KBit/Paket
SP
- = 8 Mikrosekunden
Ttrans = ------- = ------------------------------------9
C
10 Bit/s
Wenn der Sender bei unserem Stop-and-Wait-Protokoll mit dem Senden des Pakets
bei t = 0 beginnt, tritt das letzte Bit bei t = 8 Mikrosekunden auf der Senderseite in
den Kanal ein. Das Paket legt dann seine 15-ms-Reise durch das Land zurück, wie in
Abbildung 3.17 (a) dargestellt ist, wobei das letzte Bit des Pakets bei t = 15,008 ms
beim Empfänger auftaucht. Geht man der Einfachheit halber davon aus, dass die
ACK-Pakete die gleiche Größe wie die Datenpakete haben und der Empfänger mit
dem Senden eines ACK-Pakets beginnen kann, sobald das letzte Bit eines Datenpakets empfangen wurde, taucht das letzte Bit des ACK-Pakets bei t = 30,016 ms wieder
beim Sender auf. Über 30,016 ms hinweg war der Sender also tatsächlich nur 0,016 ms
(mit dem Senden oder Empfangen) beschäftigt. Wenn wir die Auslastung (Utilization) des Senders (oder Kanals) als die Zeit definieren, in der der Sender tatsächlich
mit dem Senden von Bits über den Kanal beschäftigt ist, erhalten wir eine eher
magere Senderauslastung USender von
0, 008
USender = ---------------- = 0,00015
30,016
Das heißt, dass der Sender nur 2,7 Hundertstel eines Prozents der Zeit beschäftigt
war. Aus anderer Sicht betrachtet, konnte der Sender in 30,016 Millisekunden nur
1 Kilobyte senden, was einem effektiven Durchsatz von nur 33 Kilobyte pro Sekunde
entspricht, obwohl eine Verbindungsleitung mit 1 Gigabit pro Sekunde verfügbar
war! Man stelle sich den unglücklichen Netzwerkmanager vor, der gerade ein Vermögen für eine Verbindungsleitung mit Gigabit-Kapazität ausgegeben hat und einen
Durchsatz von gerade einmal 33 Kilobyte pro Sekunde erhält! Dies ist ein anschauliches Beispiel dessen, wie Netzwerkprotokolle die von der zugrunde liegenden Netzwerkhardware zur Verfügung gestellten Kapazitäten einschränken können. Außerdem haben wir die Verarbeitungszeiten der niederschichtigen Protokolle beim Sender
und Empfänger sowie die Verarbeitungs- und Warteschlangenverzögerungen, die bei
dazwischen liegenden Routern entstehen, nicht berücksichtigt. Bezieht man diese
Verzögerungen noch mit ein, würde sich das ohnehin schon schlechte Leistungsergebnis weiter verschlechtern.
Abbildung 3.17 Gegenüberstellung eines Stop-and-Wait- und eines Pipelining-Protokolls
Datenpaket
Datenpakete
ACK-Pakete
(a) Operation eines Stop-and-Wait-Protokolls (b) Operation eines Protokolls mit Pipelining
Für dieses spezifische Leistungsproblem gibt es eine einfache Lösung: Statt im Stopand-Wait-Betrieb zu arbeiten, lässt man den Sender mehrere Pakete senden, ohne
201
Kapitel 3 – Transportschicht
202
dass er auf Bestätigungen warten muss; dieses Szenario ist in Abbildung 3.17 (b) dargestellt. Da die vielen zwischen dem Sender und dem Empfänger im Transit befindlichen Pakete als »Füllen einer Pipeline« betrachtet werden können, wird diese Technik
als Pipelining bezeichnet. Durch Pipelining ergeben sich für zuverlässige Datentransferprotokolle mehrere Konsequenzen:
õ Der Bereich der Sequenznummern muss vergrößert werden, weil jedes im Transit
befindliche Paket (ohne Neuübertragungen mitzuzählen) eine eindeutige
Sequenznummer haben muss und mehrere unbestätigte im Transit befindliche
Pakete möglich sein müssen.
õ Die Sender- und die Empfängerseite des Protokolls benötigen einen Puffer für
mehr als ein Paket. Als Minimum muss der Sender die bereits übertragenen, aber
noch nicht bestätigten Pakete zwischenspeichern. Außerdem müssen möglicherweise korrekt empfangene Pakete beim Empfänger zwischengespeichert werden
(siehe Beschreibung weiter unten).
Der Bereich der benötigten Sequenznummern und die Pufferanforderungen werden
von der Art abhängen, in der ein Datentransferprotokoll auf verlorene, beschädigte
und übermäßig verzögerte Pakete reagiert. Für die Wiederherstellung nach Fehlern
mit der Pipelining-Technik sind zwei grundlegende Ansätze bekannt: Go-Back-N
und Selective Repeat.
3.4.3 Go-Back-N (GBN)
Bei einem GBN-Protokoll (Go-Back-N) ist es dem Sender gestattet, mehrere Pakete
(sofern vorhanden) zu übertragen, ohne auf eine Bestätigung warten zu müssen. Insgesamt darf er aber nicht mehr als eine bestimmte Höchstzahl, N, an unbestätigten
Abbildung 3.18 Sequenznummern im Go-Back-N aus Sicht des Senders
base
nextseqnum
Fenstergröße
N
Bereits
bestätigt
Verwendbar, noch
nicht gesendet
Gesendet,
noch nicht
bestätigt
Nicht verwendbar
3.4 Prinzipien des zuverlässigen Datentransfers
Paketen in die Pipeline geben. Abbildung 3.18 zeigt den Bereich der Sequenznummern in einem GBN-Protokoll aus Sicht des Senders. Wenn wir base als die
Sequenznummer des ältesten unbestätigten Pakets und nextseqnum als die kleinste
nicht benutzte Sequenznummer (d. h. die Sequenznummer des als Nächstes zu sendenden Pakets) definieren, dann erhalten wir vier Intervalle in dem Sequenznummernbereich. Das Intervall [0,base-1] entspricht Paketen, die bereits übertragen
und bestätigt wurden. Das Intervall [base,nextseqnum-1] entspricht Paketen, die
bereits gesendet, aber noch nicht bestätigt wurden. Sequenznummern im Intervall
[nextseqnum, base+N-1] werden für Pakete benutzt, die sofort gesendet werden
können, wenn Daten von der höheren Schicht ankommen. Schließlich können die
Sequenznummern, die größer als oder gleich base+N sind, so lange nicht benutzt
werden, bis ein momentan in der Pipeline befindliches, nicht bestätigtes Paket
bestätigt wurde.
Wie aus Abbildung 3.18 deutlich wird, kann der Bereich der zulässigen Sequenznummern für übertragene, aber noch nicht bestätigte Pakete als »Fenster« mit Größe
N über den Sequenznummernbereich betrachtet werden. Während der Operation des
Protokolls schiebt sich dieses Fenster über den Sequenznummernbereich vorwärts.
Aus diesem Grund wird N meist als Fenstergröße (Window Size) und das GBN-Protokoll als Sliding-Window-Protokoll bezeichnet. Sie wundern sich vielleicht, warum
wir die Anzahl der schwebenden, nicht bestätigten Pakete überhaupt auf einen Wert
von N einschränken. Warum nicht eine unbegrenzte Anzahl solcher Pakete zulassen?
Abschnitt 3.5 wird zeigen, dass Flusskontrolle einer der Gründe ist, warum wir dem
Sender eine Grenze auferlegen müssen. Ein weiterer Grund wird in Abschnitt 3.7 in
Zusammenhang mit der TCP-Überlastkontrolle beschrieben.
Abbildung 3.19 Erweiterte FSM-Beschreibung des GBN-Senders
rdt_send(data)
if(nextseqnum<base+N){
compute chksum
make_pkt(sendpkt,
nextseqnum,data,chksum)
udt_send(sndpkt(nextseqnum))
if(base==nextseqnum)
start_timer
nextseqnum=nextseqnum+1
}
else
refuse_data(data)
rdt_rcv(rcv_pkt)
&& notcorrupt(rcvpkt)
base=getacknum(rcvpkt)+1
if(base==nextseqnum)
stop_timer
else
start_timer
timeout
Warte
start_timer
udt_send(sndpkt(base))
udt_send(sndpkt(base+1)
..
udt_send(sndpkt
(nextseqnum-1))
In der Praxis ist die Sequenznummer eines Pakets in einem Feld mit fester Länge im
Paket-Header enthalten. Wenn k die Anzahl der Bits im Sequenznummernfeld des
203
Kapitel 3 – Transportschicht
204
Pakets ist, lautet der Sequenznummernbereich [0,2k – 1]. Mit einem endlichen
Sequenznummernbereich muss sämtliche Arithmetik, die mit Sequenznummern zu
tun hat, mit Hilfe der Modulus-2k-Arithmetik erfolgen. (Das heißt, den Sequenznummernraum kann man sich als Ring mit der Größe 2k vorstellen, wobei der Sequenznummer 2k – 1 unmittelbar die Sequenznummer 0 folgt.) Wir erinnern uns, dass
rdt3.0 eine 1-Bit-Sequenznummer und einen Sequenznummernbereich von [0,1]
hatte. In mehreren Übungen am Ende dieses Kapitels werden die Konsequenzen
eines endlichen Sequenznummernbereichs untersucht. Wir werden in Abschnitt 3.5
noch sehen, dass TCP ein Feld für eine 32-Bit-Sequenznummer hat, wobei TCPSequenznummern anstelle von Paketen Bytes im Bytestrom zählen.
Die Abbildungen 3.19 und 3.20 zeigen eine erweiterte FSM-Beschreibung der Sender- und Empfängerseite eines GBN-Protokolls, das ACKs, jedoch keine NAKs benutzt.
Wir bezeichnen diese FSM-Beschreibung als erweiterte FSM, weil wir Variablen (ähnlich Variablen in Programmiersprachen) für base und nextseqnum sowie Operationen
und bedingte Aktionen für diese Variablen hinzugefügt haben. Man beachte, dass die
erweiterte FSM-Spezifikation allmählich wie die Spezifikation einer Programmiersprache aussieht. In [Bochman 1984] finden Sie eine ausgezeichnete Untersuchung zusätzlicher Erweiterungen für FSM-Techniken sowie andere Techniken auf der Grundlage
von Programmiersprachen für die Spezifikation von Protokollen.
Abbildung 3.20 Erweiterte FSM-Beschreibung des GBN-Empfängers
Warte
default
udt_send(sndpkt)
rdr_rcv(rcvpkt) &&
notcorrupt(rcvpkt) &&
hasseqnum(rcvpkt,expectedseqnum)
extract(rcvpkt,data)
deliver_data(data)
make_pkt(sndpkt,ACK,expectedseqnum)
udt_send(sndpkt)
Der GBN-Sender muss auf drei Ereignisarten reagieren:
õ Aufruf von oben: Wenn rdt_send() von oben aufgerufen wird, prüft der Sender
zuerst, ob das Fenster voll ist, d. h. ob N unbestätigte Pakete anstehen. Ist das
Fenster nicht voll, wird ein Paket erzeugt und gesendet und die Variablen werden
entsprechend aktualisiert. Ist das Fenster voll, gibt der Sender einfach die Daten
an die höhere Schicht zurück; dies ist ein impliziter Hinweis darauf, dass das
Fenster voll ist. Die höhere Schicht würde es dann vermutlich später erneut versuchen. In einer echten Implementierung hätte der Sender diese Daten eher zwischengespeichert (und nicht sofort gesendet) oder es wäre ein Synchronisationsmechanismus (z. B. eine Semaphore oder ein Flag) vorhanden, der es der höheren
Schicht ermöglichen würde, rdt_send() nur aufzurufen, wenn das Fenster nicht
voll ist.
õ Empfang eines ACK: Bei unserem GBN-Protokoll wird eine Bestätigung für ein
Paket mit der Sequenznummer n als kumulative Bestätigung betrachtet, was darauf hinweist, dass alle Pakete mit einer Sequenznummer bis einschließlich n korrekt beim Empfänger angekommen sind. Wir greifen dieses Thema wieder auf,
wenn wir die Empfängerseite von GBN beschreiben.
3.4 Prinzipien des zuverlässigen Datentransfers
õ Timeout-Ereignis: Der Name des Protokolls, »Go-Back-N«, leitet sich aus dem Verhalten des Senders bei verlorenen oder übermäßig verzögerten Pakete ab. Wie
beim Stop-and-Wait-Protokoll wird auch hier ein Timer benutzt, um den ordentlichen Betrieb nach einem Verlust von Daten- oder Bestätigungspaketen wieder
herzustellen. Wenn der Timer abläuft, überträgt der Sender alle Pakete, die zuvor
gesendet, aber noch nicht bestätigt wurden, noch einmal. Der Sender in Abbildung 3.19 benutzt nur einen einzigen Timer, den man sich als Timer für das älteste
übertragene, aber noch nicht bestätigte Paket vorstellen kann. Wenn ein ACK
empfangen wird, während immer noch übertragene und noch nicht bestätigte
Pakete anstehen, wird der Timer neu gestartet. Falls keine unbestätigten Pakete
anstehen, wird der Timer gestoppt.
Die Aktionen des Empfängers sind im GBN ebenfalls einfach. Wenn ein Paket mit
Sequenznummer n korrekt und in der richtigen Reihenfolge ankommt (d. h., die
zuletzt an die höhere Schicht übertragenen Daten kamen von einem Paket mit
Sequenznummer n – 1), sendet der Empfänger ein ACK für Paket n und überträgt
den Datenteil des Pakets an die höhere Schicht. In allen übrigen Fällen verwirft der
Empfänger das Paket und sendet ein weiteres ACK für das letzte, in der richtigen Reihenfolge empfangene Paket. Da Pakete einzeln an die höhere Schicht übertragen werden, wurden alle Pakete mit einer Sequenznummer, die kleiner als k ist, ebenfalls
übertragen, wenn Paket k empfangen wurde. Folglich ist die Verwendung kumulativer Bestätigungen eine natürliche Wahl für GBN.
In unserem GBN-Protokoll verwirft der Empfänger außer der Reihe ankommende
Pakete. Es mag zwar unsinnig und verschwenderisch erscheinen, ein korrekt (aber
außer der Reihe) empfangenes Paket zu verwerfen, jedoch gibt es einen guten Grund
für dieses Vorgehen. Wir erinnern uns, dass der Empfänger Daten in der richtigen Reihenfolge an die höhere Schicht weitergeben muss. Angenommen, es wird Paket n
erwartet, während aber Paket n + 1 ankommt. Da Daten in der richtigen Reihenfolge
übergeben werden müssen, könnte der Empfänger Paket n + 1 zwischenspeichern und
es dann an die höhere Schicht weitergeben, nachdem er Paket n empfangen und weitergegeben hat. Geht Paket n aber verloren, müssen den GBN-Regeln zufolge Paket n
und n + 1 letztlich erneut übertragen werden. Deshalb lassen wir den Empfänger Paket
n + 1 einfach verwerfen. Der Vorteil ist dabei die Einfachheit der Zwischenspeicherung
beim Empfänger: Dieser muss keine außer der Reihe ankommenden Pakete zwischenspeichern. Während der Sender also die obere und untere Grenze seines Fensters und
die Position von nextseqnum innerhalb dieses Fensters einhalten muss, braucht der
Empfänger als einzige Information nur die Sequenznummer des nächsten, in der richtigen Reihenfolge ankommenden Pakets zu verwalten. Dieser Wert befindet sich in der
Variablen expectedseqnum (siehe Empfänger-FSM in Abbildung 3.20). Selbstverständlich hat das Wegwerfen eines korrekt empfangenen Pakets auch einen Nachteil: Die
anschließende Neuübertragung dieses Pakets kann verloren gehen oder beschädigt
werden, so dass unter Umständen noch mehr Neuübertragungen nötig sind.
Abbildung 3.21 zeigt die Operation des GBN-Protokolls mit einer Fenstergröße
von vier Paketen. Aufgrund dieser Begrenzung der Fenstergröße überträgt der Sender die Pakete 0 bis 3 und muss dann warten, bis eines oder mehrere dieser Pakete
bestätigt werden, bevor er fortfahren kann. Während aufeinander folgende ACKs
(z. B. ACK0 und ACK1) empfangen werden, wird das Fenster vorwärts geschoben
und der Sender kann ein neues Paket (pkt4 bzw. pkt5) übertragen. Auf der Empfängerseite geht Paket 2 verloren, so dass die Pakete 3, 4 und 5 nicht in der richtigen Reihenfolge sind und verworfen werden.
205
Kapitel 3 – Transportschicht
206
Abbildung 3.21 Go-Back-N in Operation
Sender
Empfänger
pkt0 senden
pkt0 empfangen
ACK0 senden
pkt1 senden
pkt2 senden
(Verlust)
X
pkt1 empfangen
ACK1 senden
pkt3 senden
(warten)
pkt3 empfangen, verwerfen
ACK1 senden
ACK0 empfangen
pkt4 senden
ACK1 empfangen
pkt5 senden
pkt2 Timeout
pkt2 senden
pkt3 senden
pkt4 senden
pkt5 senden
pkt4 empfangen, verwerfen
ACK1 senden
pkt5 empfangen, verwerfen
ACK1 senden
pkt2
ACK2
pkt3
ACK3
empfangen, weitergeben
senden
empfangen, weitergeben
senden
Bevor wir unsere GBN-Diskussion beenden, lohnt sich an dieser Stelle der Hinweis,
dass eine Implementierung dieses Protokolls in einem Protokollstack wahrscheinlich
ähnlich aussähe wie die der erweiterten FSM in Abbildung 3.19. Die Implementierung würde wahrscheinlich ebenfalls verschiedene Prozeduren umfassen, die die
Aktionen implementieren, die als Reaktion auf die verschiedenen möglichen Ereignisse unternommen werden müssen. Bei einer derartigen ereignisbasierten Programmierung werden die verschiedenen Prozeduren entweder von anderen Prozeduren im Protokollstack oder als Ergebnis eines Interrupt aufgerufen. Im Sender
wären diese Ereignisse (1) ein Aufruf von rdt_send() durch die Instanz der höheren
Schicht, (2) ein Timer-Interrupt und (3) ein Aufruf von rdt_rcv() durch die höhere
Schicht bei Ankunft eines Pakets. Die Programmierübungen am Ende dieses Kapitels
geben Ihnen Gelegenheit, diese Routinen in einer simulierten, aber realistischen
Netzwerkumgebung zu implementieren.
Man beachte, dass das GBN-Protokoll fast alle Techniken beinhaltet, denen wir bei
der Untersuchung der zuverlässigen Datentransferkomponenten von TCP in
Abschnitt 3.5 begegnen. Diese Techniken umfassen die Verwendung von Sequenznummern, kumulative Bestätigungen, Prüfsummen und eine Timeout/Neuübertragungsoperation. In diesem Sinn weist TCP verschiedene Elemente eines GBN-Protokolls auf. Zwischen GBN und TCP existieren aber einige Unterschiede. Viele TCPImplementierungen speichern korrekt, jedoch außer der Reihenfolge empfangene
3.4 Prinzipien des zuverlässigen Datentransfers
Segmente in einem Puffer [Stevens 1994]. Eine für TCP vorgeschlagene Modifikation,
die sogenannte »selektive Bestätigung« [RFC 2581], wird es einem TCP-Empfänger
auch ermöglichen, ein bestimmtes außer der Reihe angekommenes Paket selektiv zu
bestätigen, statt kumulativ das zuletzt korrekt empfangene Paket bestätigen zu müssen. Das Konzept einer selektiven Bestätigung liegt im Kern der zweiten allgemeinen
Klasse von Pipeline-Protokollen: die so genannten »Selective-Repeat-Protokolle«
(siehe nächsten Abschnitt). TCP passt folglich scheinbar am besten in die Kategorie
eines Hybridprotokolls, d. h. einer Mischung zwischen Go-Back-N- und SelectiveRepeat-Protokollen.
3.4.4 Selective Repeat (SR)
Das GBN-Protokoll erlaubt es dem Sender, potenziell »die Pipeline in Abbildung 3.17
mit Paketen zu füllen« und daher die bei Stop-and-Wait-Protokollen auftretenden
Probleme mit der Kanalauslastung zu vermeiden. Es gibt allerdings Szenarien, in
denen GBN ebenfalls unter Leistungsproblemen leidet. Wenn die Fenstergröße und
das Bandbreite/Verzögerung-Produkt groß sind, können sich viele Pakete in der
Pipeline befinden. Ein einzelner Paketfehler kann GBN veranlassen, eine große
Anzahl von Paketen erneut zu übertragen, von denen viele vielleicht unnötig sind.
Abbildung 3.22 Sequenznummernraum aus Sicht des SR-Senders und -Empfängers
send_base
Bereits
bestätigt
Verwendbar, noch
nicht gesendet
Gesendet, noch
nicht bestätigt
Nicht verwendbar
nextseqnum
Fenstergröße
N
(a) Sequenznummern aus Sicht des Senders
Außer der Reihe
(im Puffer), aber
bereits bestätigt
Akzeptabel (innerhalb
der Fenstergröße)
Erwartet, noch
nicht empfangen
Nicht verwendbar
Fenstergröße
N
rcv_base
(b) Sequenznummern aus Sicht des Empfängers
207
208
Kapitel 3 – Transportschicht
Mit zunehmender Wahrscheinlichkeit von Kanalfehlern füllt sich die Pipeline zunehmend mit solchen unnötigen Neuübertragungen. Man stelle sich in unserem Nachrichtendiktierszenario vor, dass jedes Mal, wenn ein Wort beschädigt wird, die umgebenden 1.000 Wörter (bei einer Fenstergröße von beispielsweise 1.000 Wörtern)
wiederholt werden müssten. Das Diktat würde sich durch all die Wortwiederholungen sehr verlangsamen.
Wie die Bezeichnung bereits andeutet, vermeiden SR-Protokolle (SelectiveRepeat) unnötige Neuübertragungen dadurch, dass der Sender nur jene Pakete
erneut überträgt, bei denen er einen fehlerhaften Empfang (verloren oder beschädigt)
auf Seiten des Empfängers vermutet. Diese selektive Neuübertragung setzt voraus,
dass der Empfänger korrekt empfangene Pakete individuell bestätigt. Eine Fenstergröße von N wird wiederum benutzt, um die Anzahl der ausstehenden unbestätigten
Pakete in der Pipeline zu begrenzen. Im Gegensatz zu GBN hat der Sender aber für
einige der im Fenster stehenden Pakete bereits ACKs empfangen. Abbildung 3.22
zeigt den Sequenznummernraum aus Sicht des SR-Senders. Die verschiedenen, vom
SR-Sender unternommenen Aktionen sehen Sie in Abbildung 3.23.
Der SR-Empfänger bestätigt ein korrekt empfangenes Paket ungeachtet dessen, ob
es in der richtigen oder falschen Reihenfolge ankommt. Außer der Reihe ankommende Pakete werden so lange zwischengespeichert, bis eventuell fehlende Pakete
(d. h. Pakete mit niedrigeren Sequenznummern) empfangen werden. An diesem
Punkt kann eine Paketserie in der richtigen Reihenfolge an die höhere Schicht weitergegeben werden. Abbildung 3.24 beschreibt die Einzelheiten der verschiedenen, vom
SR-Empfänger eingeleiteten Aktionen. Abbildung 3.25 zeigt ein Beispiel der SR-Operation für den Fall verlorener Pakete. Man beachte in Abbildung 3.25, dass der Empfänger die Pakete 3 und 4 zunächst zwischenspeichert und sie dann zusammen mit
Paket 2, nachdem dieses eingegangen ist, an die höhere Schicht weitergibt.
Abbildung 3.23 Ereignisse und Aktionen des SR-Senders
1. Daten werden von oben empfangen. Wenn Daten von oben empfangen werden, prüft
der SR-Sender die nächste, für das Paket verfügbare Sequenznummer. Liegt die
Sequenznummer innerhalb des Senderfensters, werden die Daten in einem Paket
gekapselt und gesendet; andernfalls werden sie entweder zwischengespeichert oder
zur späteren Übertragung, wie in GBN, an die höhere Schicht zurückgegeben.
2. Timeout. Auch hier werden als Schutz vor verlorenen Paketen Timer benutzt. Jedes
Paket braucht aber einen eigenen logischen Timer, weil bei einem Timeout nur jeweils
ein einzelnes Paket übertragen wird. Ein einziger Hardware-Timer lässt sich verwenden,
um mehrere logische Timer vorzutäuschen [Varghese 1997].
3. ACK empfangen. Wenn ein ACK empfangen wird, markiert der SR-Sender dieses Paket
als empfangen, sofern es sich im Fenster befindet. Ist die Sequenznummer des Pakets
gleich send-base, rückt die Fensterbasis zu dem unbestätigten Paket mit der kleinsten
Sequenznummer vor. Wenn das Fenster vorrückt, während noch unübertragene Pakete
mit Sequenznummern anstehen, die jetzt innerhalb des Fensters liegen, werden diese
Pakete übertragen.
Wichtig ist in Schritt 2 von Abbildung 3.24, dass der Empfänger bereits empfangene
Pakete mit bestimmten Sequenznummern unter der aktuellen Fensterbasis erneut
bestätigt (statt sie zu ignorieren). Sie sollten sich selbst davon überzeugen, dass diese
erneute Bestätigung tatsächlich nötig ist. Wenn sich beispielsweise im Fall des
3.4 Prinzipien des zuverlässigen Datentransfers
Sequenznummernraums des Senders und des Empfängers in Abbildung 3.22 kein
ACK für Paket send_base vom Empfänger zum Sender ausbreitet, überträgt der Sender letztlich das Paket send_base erneut, obwohl uns (nicht aber dem Sender!) klar
ist, dass der Empfänger dieses Paket bereits empfangen hat. Wenn der Empfänger
dieses Paket nicht bestätigen würde, würde das Fenster des Senders nie vorrücken!
Dieses Beispiel zeigt einen wichtigen Aspekt von SR-Protokollen (und auch vieler
anderer Protokolle). Sender und Empfänger haben nicht immer die gleiche Sicht dessen, was korrekt empfangen wurde. Für SR-Protokolle bedeutet das, dass das Senderund das Empfängerfenster nicht immer übereinstimmen.
Abbildung 3.24 Ereignisse und Aktionen des SR-Empfängers
1. Paket mit Sequenznummer in [rcv_base, rcv_base+N–1] korrekt empfangen: In diesem Fall liegt das empfangene Paket innerhalb des Empfängerfensters und ein selektives ACK-Paket wird an den Sender zurückgegeben. Wenn das Paket nicht zuvor empfangen wurde, wird es zwischengespeichert. Hat dieses Paket eine Sequenznummer
gleich der Basis des Empfangsfensters (rcv_base in Abbildung 3.22), dann werden
dieses und eventuell zuvor zwischengespeicherte und (beginnend mit rcv_base) durchnummerierte Pakete an die höhere Schicht weitergereicht. Das Empfangsfenster wird
dann um die Anzahl der an die höhere Schicht weitergereichten Pakete vorwärts
geschoben. Als Beispiel betrachte man Abbildung 3.25. Wenn ein Paket mit einer
Sequenznummer von rcv_base=2 empfangen wird, können dieses und die Pakete
rcv_base+1 und rcv_base2 an die höhere Schicht weitergegeben werden.
2. Paket mit Sequenznummer in [rcv_base–N, rcv_base–1] empfangen: In diesem Fall
muss ein ACK generiert werden, auch wenn dies ein Paket ist, das der Empfänger
bereits bestätigt hat.
3. Andernfalls: Ignoriere das Paket.
Der Mangel an Synchronisation zwischen dem Sender- und Empfängerfenster hat
wichtige Konsequenzen, wenn wir mit der Realität eines endlichen Sequenznummernbereichs konfrontiert sind. Man betrachte, was beispielsweise mit einem endlichen Bereich von vier Paketsequenznummern, 0, 1, 2 und 3, und einer Fenstergröße
von 3 passiert. Angenommen, die Pakete 0 bis 2 werden übertragen, beim Empfänger
korrekt empfangen und bestätigt. An diesem Punkt befindet sich das Empfängerfenster über dem vierten, fünften und sechsten Paket, die die Sequenznummern 3, 0 bzw.
1 haben. Nun betrachte man zwei Szenarien. Im ersten Szenario, das in Abbildung
3.26 (a) dargestellt ist, gehen die ACKs für die ersten drei Pakete verloren und der
Sender überträgt diese Pakete erneut. Der Empfänger empfängt also als Nächstes ein
Paket mit der Sequenznummer 0 – eine Kopie des ersten gesendeten Pakets.
Im zweiten Szenario, das in Abbildung 3.26 (b) dargestellt ist, wurden die ACKs
für die ersten drei Pakete korrekt weitergegeben. Der Sender schiebt folglich sein
Fenster weiter und sendet das vierte, fünfte und sechste Paket mit den Sequenznummern 3, 0 bzw. 1. Das Paket mit Sequenznummer 3 geht verloren, während jedoch
dasjenige mit Sequenznummer 0 – ein Paket, das neue Daten enthält – ankommt.
Abbildung 3.26 zeigt die Situation aus Sicht des Empfängers; es wurde eine Art
»Vorhang« zwischen Sender und Empfänger eingefügt, da der Empfänger die vom
Sender eingeleiteten Aktionen nicht »sehen« kann. Alles, was der Empfänger mitbekommt, ist die Sequenz von Nachrichten, die er vom Kanal empfängt und in den
Kanal sendet. Was ihn anbelangt, sind die beiden Szenarien in Abbildung 3.26 iden-
209
Kapitel 3 – Transportschicht
210
Abbildung 3.25 SR-Operation
pkt0 gesendet
0 1 2 3 4 5 6 7 8 9
pkt1 gesendet
pkt0 empfangen,abgegeben,ACK0 gesendet
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
pkt2 gesendet
pkt1 empfangen,abgegeben,ACK1 gesendet
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
X(Verlust)
pkt3 gesendet, Fenster voll
0 1 2 3 4 5 6 7 8 9
pkt3 empfangen,zwischengesp.,ACK3 gesendet
0 1 2 3 4 5 6 7 8 9
ACK0 empfangen, pkt4 gesendet
0 1 2 3 4 5 6 7 8 9
pkt4 empfangen,zwischengesp.,ACK4 gesendet
pkt2 Timeout, pkt2 erneut gesendet
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
ACK1 empfangen, pkt5 gesendet
pkt2 empfangen;pkt2,pkt3,pkt4
abgegeben,ACK4 gesendet
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
pkt5 empfangen,abgegeben,ACK5 gesendet
0 1 2 3 4 5 6 7 8 9
tisch. Er hat keine Möglichkeit der Unterscheidung zwischen der Neuübertragung
des ersten Pakets und der erstmaligen Übertragung des fünften Pakets. Natürlich
kann eine Fenstergröße, die um 1 kleiner als die Größe des Sequenznummernraums
ist, nicht funktionieren. Wie klein muss aber die Fenstergröße sein? In einer Übung
am Ende dieses Kapitels sollen Sie aufzeigen, dass die Fenstergröße für SR-Protokolle
kleiner als oder gleich die Hälfte der Größe des Sequenznummernraums sein muss.
Wir beenden unsere Diskussion zuverlässiger Datentransferprotokolle mit einer
verbleibenden Annahme in unserem grundlegenden Kanalmodell. Wie Sie sich sicher
erinnern, sind wir davon ausgegangen, dass Pakete innerhalb des Kanals zwischen
Sender und Empfänger nicht umgeordnet werden können. Dies ist im Allgemeinen
eine vernünftige Annahme, wenn der Sender und der Empfänger über eine einzige
physikalische Leitung verbunden sind. Ist der »Kanal«, der die beiden verbindet,
aber ein Netzwerk, kann eine Paketumstellung erfolgen. Eine Folge der Umordnung
von Paketen ist, dass alte Kopien eines Pakets mit einer Sequenz- oder Bestätigungsnummer von x auftauchen können, auch wenn weder das Sender- noch das Empfängerfenster x enthält. Im Sinne der Paketumstellung kann man sich den Kanal so vorstellen, dass er im Wesentlichen Pakete zwischenspeichert und diese dann spontan
irgendwann in der Zukunft ausgibt. Da Sequenznummern wiederverwendet werden
können, bedarf es einiger Sorgfalt, um solche Duplikatpakete zu verhindern. Mit dem
in der Praxis angewandten Ansatz wird sichergestellt, dass eine Sequenznummer erst
wiederverwendet wird, wenn der Sender relativ »sicher« ist, dass zuvor gesendete
Pakete mit Sequenznummer x nicht mehr im Netzwerk sind. Dies gründet auf der
Annahme, dass ein Paket im Netzwerk nicht länger als eine bestimmte Höchstdauer
»leben« kann. Eine maximale Lebensdauer für Pakete von ungefähr drei Minuten
3.4 Prinzipien des zuverlässigen Datentransfers
Abbildung 3.26 Dilemma des SR-Empfängers mit zu großen Fenstern: ein neues Paket oder
eine Neuübertragung?
Senderfenster
(nach Empfang)
Empfängerfenster
(nach Empfang)
0 1 2 3 0 1 2
pkt0
0 1 2 3 0 1 2
pkt1
0 1 2 3 0 1 2
pkt2
ACK0 0 1 2 3 0 1 2
ACK1 0 1 2 3 0 1 2
ACK2
x
0 1 2 3 0 1 2
x
Timeout
Neuübertragung pkt0 x
0 1 2 3 0 1 2
pkt0
empfange Paket
mit Sequenznummer 0
(a)
Senderfenster
(nach Empfang)
Empfängerfenster
(nach Empfang)
0 1 2 3 0 1 2
pkt0
0 1 2 3 0 1 2
pkt1
0 1 2 3 0 1 2
pkt2
ACK0 0 1 2 3 0 1 2
ACK1 0 1 2 3 0 1 2
ACK2
0 1 2 3 0 1 2
pkt3
0 1 2 3 0 1 2
pkt0
0 1 2 3 0 1 2
x
empfange Paket
mit Sequenznummer 0
(b)
211
212
Kapitel 3 – Transportschicht
wird in den TCP-Erweiterungen für Hochgeschwindigkeitsnetze [RFC 1323] angenommen. In [Sunshine 1978] wird eine Methode für die Verwendung von Sequenznummern beschrieben, bei der Umordnungsprobleme völlig umgangen werden
können.
3.5 Verbindungsorientierter Transport: TCP
Nachdem wir die grundlegenden Prinzipien von zuverlässigem Datentransfer untersucht haben, wenden wir uns nun TCP – dem verbindungsorientierten, zuverlässigen
Internet-Transportprotokoll – zu. Um einen zuverlässigen Datentransfer bereitzustellen, nutzt TCP viele der grundlegenden Prinzipien, die im vorherigen Abschnitt
beschrieben wurden, darunter Fehlererkennung, Neuübertragungen, kumulative
Bestätigungen, Timer und Header-Felder für Sequenz- und Bestätigungsnummern.
TCP ist in RFC 793, RFC 1122, RFC 1323, RFC 2018 und RFC 2581 definiert.
3.5.1 Die TCP-Verbindung
TCP bietet Multiplexen, Demultiplexen und Fehlererkennung auf genau die gleiche
Weise wie UDP. Dennoch unterscheiden sich TCP und UDP auf vielerlei Art. Der
wichtigste Unterschied ist, dass UDP verbindungslos und TCP verbindungsorientiert ist. UDP ist verbindungslos, weil es Daten sendet, ohne eine Verbindung aufzubauen. TCP ist verbindungsorientiert, weil zwei Prozesse zuerst ein »Handshake«
durchführen müssen, bevor sie einander Daten senden können. Das heißt, sie müssen
einige Segmente austauschen, um die Parameter für den anschließenden Datentransfer einzurichten. Als Teil des TCP-Verbindungsaufbaus initialisieren beide Seiten der
Verbindung viele »TCP-Zustandsvariablen« (von denen viele in diesem Abschnitt und
in Abschnitt 3.7 beschrieben werden) in Zusammenhang mit der TCP-Verbindung.
Die »TCP-Verbindung« ist keine Ende-zu-Ende-TDM- oder -FDM-Leitung wie in
einem leitungsvermittelten Netzwerk. Sie ist auch kein virtueller Kanal (siehe Kapitel
1), weil der Verbindungszustand vollständig in den beiden Endsystemen residiert. Da
das TCP-Protokoll nur in den Endsystemen und nicht in den dazwischen liegenden
Netzwerkelementen (Routern und Bridges) läuft, führen die dazwischen liegenden
Netzwerkelemente keinen TCP-Verbindungszustand. Tatsächlich haben die vermittelnden Router keine Ahnung von TCP-Verbindungen; sie sehen Datagramme, aber
keine Verbindungen.
FALLBEISPIEL
Vinton Cerf, Robert Kahn und TCP/IP
Anfang der siebziger Jahre begannen sich paketvermittelte Netzwerke zu verbreiten; ARPANET, der Vorläufer des Internets, war eines davon. Jedes dieser Netzwerke hatte sein
eigenes Protokoll. Die beiden Wissenschaftler Vinton Cerf und Robert Kahn erkannten die
Bedeutung des Zusammenschlusses dieser Netzwerke und erfanden ein netzwerkübergreifendes Protokoll namens »TCP/IP« (Transmission Control Protocol/Internet Protocol).
Obwohl Cerf und Kahn das Protokoll anfangs als eine Einheit betrachteten, wurde es später in seine zwei Teile – TCP und IP – aufgeteilt, die getrennt arbeiten. Cerf und Kahn veröffentlichten im Mai 1974 in IEEE Transactions on Communications Technology eine
Arbeit über TCP/IP.
➔
3.5 Verbindungsorientierter Transport: TCP
➔
Das TCP/IP-Protokoll, das Brot und Salz im heutigen Internet, wurde entwickelt, bevor es
PCs und Workstations, Ethernet- und andere LAN-Technologien, das Web, StreamingAudio und Chat gab. Cerf und Kahn erkannten die Notwendigkeit für ein Netzwerkprotokoll, das einerseits breite Unterstützung für künftige Anwendungen und andererseits die
Zusammenarbeit zwischen beliebigen Hosts und Protokollen der Sicherungsschicht
ermöglicht.
Eine TCP-Verbindung bietet Vollduplex-Datentransfer. Existiert eine TCP-Verbindung zwischen Prozess A auf einem Host und Prozess B auf einem anderen, dann
können die Daten der Anwendungsebene von A nach B und von B nach A gleichzeitig fließen. Eine TCP-Verbindung arbeitet auch immer Punkt-zu-Punkt, d. h. zwischen einem einzigen Sender und einem einzigen Empfänger. Das sogenannte »Multicasting« (siehe Abschnitt 4.8) – die Übertragung von Daten von einem Sender an
viele Empfänger in einer einzigen Sendeoperation – ist in TCP nicht möglich. Für
TCP sind zwei Hosts schon eine Firma und drei eine Masse!
Wir betrachten jetzt den Aufbau einer TCP-Verbindung. Angenommen, ein Prozess, der auf einem Host läuft, möchte eine Verbindung zu einem anderen Prozess auf
einem anderen Host einleiten. Wir erinnern uns, dass der Prozess, der die Verbindung
einleitet, als Client-Prozess und der andere als Server-Prozess bezeichnet wird. Der
Client-Prozess informiert zuerst das clientseitige TCP darüber, dass er eine Verbindung zu einem Prozess im Server aufbauen will. Wie in Abschnitt 2.6 beschrieben,
bewirkt dies ein Java-Client-Programm durch Abarbeitung des Befehls
Socket clientSocket = new Socket(“hostname“, port number);
Die Transportschicht im Client baut dann eine TCP-Verbindung zu TCP im Server
auf. Wir beschreiben die Prozedur des Verbindungsaufbaus ausführlicher am Ende
dieses Abschnitts. Vorläufig genügt es zu wissen, dass der Client zunächst ein spezielles TCP-Segment sendet; der Server antwortet mit einem zweiten speziellen TCPSegment; schließlich antwortet der Client wieder mit einem dritten speziellen Segment. Die ersten beiden Segmente enthalten keine Nutzdaten, d. h. keine Anwendungsdaten; das dritte kann Nutzdaten enthalten. Da drei Segmente zwischen den
beiden Hosts gesendet werden, nennt man diesen Verbindungsaufbau auch DreiWege-Handshake.
Nachdem eine TCP-Verbindung aufgebaut wurde, können die beiden Anwendungsprozesse einander Daten senden; dies ist die ganze Zeit möglich, weil TCP im
Vollduplex läuft. Wir betrachten nun genauer, wie Daten vom Client- zum ServerProzess gesendet werden. Der Client-Prozess gibt einen Datenstrom durch das Socket
(die Tür des Prozesses) weiter, wie in Abschnitt 2.6 beschrieben. Nachdem die Daten
durch die Tür geflossen sind, liegen sie in der Hand von TCP, das auf dem Client
läuft. Wie in Abbildung 3.27 dargestellt ist, leitet TCP diese Daten an den Sendepuffer der Verbindung weiter. Das ist einer der Puffer, die während des anfänglichen
Drei-Wege-Handshake vorbereitet werden. Von Zeit zu Zeit nimmt TCP Datenstücke
aus dem Sendepuffer. Interessant ist, dass die TCP-Spezifikation [RFC 793] »sehr
locker« dahingehend ist, wann TCP tatsächlich zwischengespeicherte Daten senden
sollte. Es wird lediglich angegeben, dass TCP »Daten in Segmenten nach eigenem
Ermessen senden« sollte. Die maximale Datenmenge, die aus dem Puffer genommen
und in ein Segment gestellt werden kann, ist durch die maximale Segmentgröße
(Maximum Segment Size, MSS) begrenzt. Die MSS hängt von der TCP-Implementie-
213
Kapitel 3 – Transportschicht
214
rung (die vom Betriebssystem bestimmt wird) ab und kann häufig konfiguriert werden; übliche Werte sind 1.500, 536 und 512 Byte. (Diese Segmentgrößen werden oft
gewählt, um IP-Fragmentierung (siehe nächstes Kapitel) zu vermeiden.) Man
beachte, dass die MSS die maximale Menge von Anwendungsdaten im Segment und
nicht die maximale Größe des TCP-Segments – einschließlich der Header – ist. (Diese
Terminologie ist verwirrend, wir müssen aber damit leben, weil sie sich allgemein
durchgesetzt hat.)
TCP kapselt jedes Client-Datenstück mit einem TCP-Header und formt damit
TCP-Segmente. Die Segmente werden nach unten an die Vermittlungsschicht weitergereicht, wo sie getrennt in IP-Datagrammen der Vermittlungsschicht verkapselt werden. Anschließend werden die IP-Datagramme im Netzwerk versendet. Wenn TCP
am anderen Ende ein Segment empfängt, stellt es die Daten des Segments in den
Empfangspuffer der TCP-Verbindung. Die Anwendung liest den Datenstrom aus
diesem Puffer. Jede Seite der Verbindung hat ihren eigenen Sendepuffer und ihren
eigenen Empfangspuffer. Die Sende- und Empfangspuffer für Daten, die in eine Richtung fließen, sind in Abbildung 3.27 dargestellt.
Abbildung 3.27 Die Sende- und Empfangspuffer von TCP
Prozess
Prozess
schreibt
liest
Daten
Daten
Socket
Socket
TCPSendepuffer
Segment
TCP
receive
buffer
TCPEmpfangspuffer
Eine TCP-Verbindung setzt sich also zusammen aus Puffern, Variablen und einer
Socket-Verbindung zu einem Prozess in einem Host sowie einer weiteren Gruppe von
Puffern, Variablen und einer Sokket-Verbindung zu einem Prozess auf einem anderen
Host. Wie erwähnt, werden der Verbindung in den Netzwerkelementen (Router,
Bridges und Repeater) zwischen den Hosts keine Puffer oder Variablen zugewiesen.
3.5.2 TCP-Segmentstruktur
Nach einem kurzen Überblick über die TCP-Verbindung betrachten wir nun die TCPSegmentstruktur. Das TCP-Segment besteht aus Header-Feldern und einem Datenfeld. Das Datenfeld enthält einen Teil der Anwendungsdaten. Wie erwähnt, begrenzt
die MSS die maximale Größe des Datenfelds eines Segments. Wenn TCP eine große
Datei, z. B. ein kodiertes Bild als Teil einer Web-Seite, sendet, teilt es die Datei normalerweise in Stücke gemäß der MSS-Größe auf (mit Ausnahme des letzten Stücks, das
meist kleiner als die MSS ist). Interaktive Anwendungen übertragen aber oft Datenstücke, die viel kleiner als die MSS sind; z. B. ist das Datenfeld im TCP-Segment bei
3.5 Verbindungsorientierter Transport: TCP
Remote-Login-Anwendungen wie Telnet oft nur ein Byte groß. Da der TCP-Header
generell 20 Byte (12 Byte mehr als der UDP-Header) umfasst, sind die von Telnet
gesendeten Segmente unter Umständen nur 21 Byte lang.
Abbildung 3.28 Die TCP-Segmentstruktur
Portnummer Ziel
Portnummer Quelle
Sequenznummer
Bestätigungsnummer
U A P P S F
Header- nicht
Länge benutzt R C S S Y I
G K H T N N
Internet-Prüfsumme
Empfänger-Fenstergröße
Zeiger zu dringenden Daten
Optionen
Daten
32 Bit
Abbildung 3.28 zeigt die Struktur des TCP-Segments. Wie bei UDP beinhaltet der
Header die Portnummer für Quelle und Ziel, die für das Multiplexen/Demultiplexen von Daten von/zu Anwendungen der höheren Schicht benutzt werden. Ebenfalls
wie bei UDP enthält der Header ein Prüfsummenfeld. Ein TCP-Segment-Header
beinhaltet außerdem folgende Felder:
õ Die beiden 32-Bit-Felder Sequenznummer und Bestätigungsnummer werden
vom TCP-Sender und -Empfänger benutzt, um den weiter unten beschriebenen
zuverlässigen Datentransferdienst zu implementieren.
õ Das 16-Bit-Feld Fenstergröße dient der Flusskontrolle. Wir werden in Kürze
sehen, dass es verwendet wird, um die Anzahl von Bytes anzugeben, die ein Empfänger anzunehmen bereit ist.
215
216
Kapitel 3 – Transportschicht
õ Das 4-Bit-Feld Header-Länge spezifiziert die Länge des TCP-Headers in 32-BitWörtern. Er kann aufgrund der weiter unten beschriebenen TCP-Optionen eine
variable Länge haben. (Normalerweise ist das Feld »Optionen« leer, so dass die
Länge des typischen TCP-Headers 20 Byte beträgt.)
õ Das optionale Feld Optionen mit variabler Länge wird benutzt, wenn ein Sender
und ein Empfänger die maximale Segmentgröße (MSS) vereinbaren, oder es dient
in Hochgeschwindigkeitsnetzwerken als Fensterskalierfaktor. Ferner ist eine Zeitstempeloption definiert; siehe RFC 854 und RFC 1323 mit weiteren Einzelheiten.
õ Das Flag-Feld enthält 6 Bit. Das ACK-Bit spezifiziert, dass der im Bestätigungsfeld enthaltene Wert gültig ist. Die Bits RST, SYN und FIN werden für den Aufund Abbau der Verbindung benutzt. Ist das PSH-Bit gesetzt, weiß der Empfänger,
dass er die Daten sofort an die höhere Schicht weiterreichen soll. Das URG-Bit
spezifiziert, dass es in diesem Segment Daten gibt, die von der Instanz der höheren Schicht auf der Sendeseite als »dringend« (urgent) markiert wurden. Die Position des letzten Bytes dieser dringenden Daten wird durch einen 16-Bit-Datenzeiger gekennzeichnet. TCP muss die Instanz der höheren Schicht auf der
Empfangsseite informieren, wenn dringende Daten vorliegen, und einen Zeiger
(Pointer) zur Kennzeichnung des Endes der dringenden Daten bereitstellen. (In
der Praxis werden PSH, URG und Zeiger auf dringende Daten nicht benutzt. Wir
erwähnen die Felder hier lediglich der Vollständigkeit halber.)
3.5.3 Sequenz- und Bestätigungsnummern
Zwei der wichtigsten Felder im TCP-Segment-Header enthalten die Sequenz- und die
Bestätigungsnummer. Diese beiden Felder sind ein wichtiger Teil des zuverlässigen
Datentransferdienstes von TCP. Bevor wir die Verwendung dieser Felder für die
Bereitstellung eines zuverlässigen Datentransfers beschreiben, erklären wir, was TCP
eigentlich in diese Felder einfügt.
Für TCP sind Daten ein unstrukturierter, aber geordneter Bytestrom. Die Verwendung von Sequenznummern in TCP spiegelt dies dahingehend wider, dass Sequenznummern den übertragenen Bytestrom und nicht die Serie übertragener Segmente
betreffen. Die Sequenznummer eines Segments ist die Bytestromnummer des ersten
Bytes im Segment. Wir verdeutlichen dies an einem Beispiel. Angenommen, ein Prozess in Host A möchte einen Datenstrom über eine TCP-Verbindung an einen Prozess
in Host B senden. Das TCP in Host A nummeriert jedes Byte im Datenstrom implizit.
Wenn der Datenstrom aus einer Datei mit 500.000 Byte besteht, die MSS also 1.000
Byte beträgt, und das erste Byte des Datenstroms mit Null nummeriert wird, bildet
TCP aus dem Datenstrom 500 Segmente (siehe Abbildung 3.29). Dem ersten Segment
wird die Sequenznummer 0, dem zweiten die Sequenznummer 1.000, dem dritten die
Sequenznummer 200 usw. zugewiesen. Jede Sequenznummer wird in das Sequenznummernfeld im Header des entsprechenden TCP-Segments eingefügt.
Wir untersuchen jetzt, was es mit den Bestätigungsnummern auf sich hat. Sie sind
etwas kniffliger als Sequenznummern. Wie erwähnt, arbeitet TCP in Vollduplex, so
dass Host A vielleicht Daten von Host B empfängt, während er (über die gleiche TCPVerbindung) Daten an Host B sendet. Jedes Segment, das von Host B ankommt, hat
eine Sequenznummer für die Daten, die von B nach A fließen. Die Bestätigungsnummer, die Host A in sein Segment einfügt, ist die Sequenznummer des nächsten Bytes, das Host
A von Host B erwartet. Dies lässt sich leichter anhand einiger Beispiele verstehen.
Angenommen, Host A hat alle von 0 bis 535 durchnummerierten Bytes von B emp-
3.5 Verbindungsorientierter Transport: TCP
217
Abbildung 3.29 Aufteilung der Daten einer Datei in TCP-Segmente
Datei
0
1
...
Daten des
ersten Segments
1.000
...
1.999
...
499.999
Daten des
zweiten Segments
fangen und sendet seinerseits gerade ein Segment an Host B. Anders ausgedrückt,
Host A wartet auf Byte 536 und alle nachfolgenden Bytes im Datenstrom von Host B.
Host A setzt also 536 in das Bestätigungsnummernfeld des Segments, das er an B
sendet.
Als weiteres Beispiel nehmen wir an, dass Host A ein Segment, das die Bytes 0 bis
535 enthält, und ein weiteres mit den Bytes 900 bis 1.000, von Host B empfangen hat.
Aus irgendeinem Grund sind die Bytes 536 bis 899 bei Host A noch nicht angekommen. In diesem Fall wartet Host A immer noch auf Byte 536 (und folgende), um den
Datenstrom von B wiederherstellen zu können. Das nächste Segment von A an B
wird also 536 im Bestätigungsnummernfeld enthalten. Da TCP Bytes nur bis zum ersten fehlenden Byte im Datenstrom bestätigt, sagt man, dass TCP kumulative Bestätigungen verwendet.
Das letzte Beispiel führt uns zu einem wichtigen und kniffligen Thema. Host A
hat das dritte Segment (Bytes 900 bis 1.000) vor dem zweiten Segment (Bytes 536 bis
899) empfangen. Das dritte Segment ist also außerhalb der Reihenfolge angekommen.
Das Knifflige daran ist: Was macht ein Host, wenn er Segmente in einer TCP-Verbindung außer der Reihe empfängt? Interessant ist, dass die TCP-RFCs hierfür keine
Regeln aufstellen und die Entscheidung den Leuten überlassen, die eine TCP-Implementierung programmieren. Im Grunde bestehen zwei Möglichkeiten: Entweder verwirft der Empfänger die außer der Reihe befindlichen Bytes sofort oder er behält die
außer der Reihe befindlichen Bytes und wartet auf die fehlenden Bytes, um die
Lücken zu füllen. Natürlich ist die zweite Möglichkeit hinsichtlich der Netzwerkbandbreite effizienter, während die erste den TCP-Code vereinfacht. Im restlichen Teil
dieser TCP-Einführung konzentrieren wir uns auf die erste Implementierung, d. h.,
wir gehen davon aus, dass der TCP-Empfänger außer der Reihe ankommende Segmente verwirft.
In Abbildung 3.29 sind wir davon ausgegangen, dass die anfängliche Sequenznummer Null ist. In Wirklichkeit wählen beide Seiten einer TCP-Verbindung zufällig
eine Anfangssequenznummer aus. Dies geschieht, um möglichst auszuschließen,
dass ein Segment, das noch von einer früheren, bereits beendeten Verbindung zwischen zwei Hosts im Netzwerk vorhanden ist und irrtümlich als gültiges Segment
einer späteren Verbindung zwischen den beiden gleichen Hosts (die zufällig auch die
gleichen Portnummern wie bei der alten Verbindung benutzen) betrachtet wird [Sunshine 1978].
218
Kapitel 3 – Transportschicht
3.5.4 Telnet: Eine Fallstudie für Sequenz- und
Bestätigungsnummern
Das in RFC 854 definierte Telnet ist ein beliebtes Protokoll der Anwendungsschicht,
das für Remote-Login benutzt wird. Es läuft über TCP und dem Design zufolge zwischen zwei Hosts. Im Gegensatz zu Anwendungen, die Massendaten übertragen
(siehe Kapitel 2), ist Telnet eine interaktive Anwendung. Wir beschreiben Telnet hier,
weil es ein nützliches Beispiel ist, um die Sequenz- und Bestätigungsnummern in
TCP weiter auszuleuchten.
Angenommen, Host A leitet eine Telnet-Sitzung zu Host B ein. Da Host A die Sitzung einleitet, ist er der Client, während Host B der Server ist. Jedes vom Benutzer
(am Client) eingegebene Zeichen wird an den entfernten Host gesendet. Der entfernte
Host sendet dann eine Kopie jedes Zeichens zurück, das am Bildschirm des TelnetBenutzers angezeigt wird. Dieses »Echo« wird benutzt, um sicherzustellen, dass die
Zeichen, die der Telnet-Benutzer sieht, am entfernten Standort bereits empfangen
und verarbeitet wurden. Jedes Zeichen überquert also das Netzwerk zweimal zwischen dem Moment, in dem der Benutzer die Taste drückt, und dem Moment, wenn
das Zeichen auf dem Bildschirm des Benutzers angezeigt wird.
Wir nehmen jetzt an, dass der Benutzer einen einzigen Buchstaben, C, eintippt
und sich einen Kaffee holt. Wir wollen die TCP-Segmente untersuchen, die zwischen
dem Client und dem Server gesendet werden. Wie in Abbildung 3.30 dargestellt,
gehen wir davon aus, dass die Anfangssequenznummern für den Client bzw. den
Server 42 und 79 sind. Wir erinnern uns, dass die Sequenznummer eines Segments
diejenige des ersten Bytes im Datenfeld ist. Folglich hat das erste, vom Client gesendete Segment die Sequenznummer 42 und das erste vom Server gesendete die
Sequenznummer 79. Wir wissen, dass die Bestätigungsnummer die Sequenznummer
des nächsten Datenbyte ist, auf das der Host wartet. Nach dem Aufbau der TCP-Verbindung, jedoch vor dem Versenden von Daten, wartet der Client auf Byte 79 und der
Server auf Byte 42.
Wie Sie sehen, werden in Abbildung 3.30 drei Segmente gesendet. Das erste Segment wird vom Client zum Server gesendet; es enthält in seinem Datenfeld die
1-Byte-ASCII-Darstellung des Buchstabens ‚C’. Im ersten Segment steht auch 42 im
Sequenznummernfeld, wie oben beschrieben. Da der Client noch keine Daten vom
Server erhalten hat, steht im Bestätigungsnummernfeld dieses ersten Segments 79.
Das zweite Segment wird vom Server zum Client gesendet. Es dient einem doppelten Zweck. Erstens stellt es eine Bestätigung für die Daten dar, die der Server empfangen hat. Durch Angabe der Nummer 43 im Bestätigungsfeld teilt der Server dem
Client mit, dass er alles bis einschließlich Byte 42 gut empfangen hat und nun auf
Byte 43 und folgende wartet. Der zweite Zweck dieses Segments ist die Rücksendung
von ‚C’ als »Echo«. Folglich hat das zweite Segment in seinem Datenfeld die ASCIIDarstellung von ‚C’. Dieses zweite Segment trägt die Sequenznummer 79, also die
Anfangssequenznummer des Datenflusses vom Server zum Client dieser TCP-Verbindung, weil es sich um das erste Datenbyte handelt, das der Server sendet. Man
beachte, dass die Bestätigung für die Daten vom Client zum Server in einem Segment
gesendet wird, das die Daten vom Server zum Client befördert. Aus diesem Grund
wird dies als Huckepack (Piggyback) auf dem Datensegment vom Server zum Client
bezeichnet.
Das dritte Segment wird vom Client zum Server gesendet. Es erfüllt den ausschließlichen Zweck, die vom Server empfangenen Daten zu bestätigen. (Das zweite
3.5 Verbindungsorientierter Transport: TCP
219
Abbildung 3.30 Sequenz- und Bestätigungsnummern in einer einfachen Telnet-Anwendung
über TCP
Host B
Host A
Benutzer
tippt
'C'
Host
bestätigt
Empfang
von 'C'
Seq=42
, ACK=
79, Da
ten='C
'
'
en='C
3, Dat
4
=
K
C
9, A
Seq=7
Host
bestätigt
Empfang von
'C' und gibt
Echo zurück
Seq=43
, ACK=
80
Zeit
Segment enthält die Daten – den Buchstaben ‚C’ – vom Server zum Client.) Dieses
Segment enthält ein leeres Datenfeld (d. h., die Bestätigung wird nicht huckepack mit
den Daten vom Client zum Server gesendet). Das Segment hat 80 in seinem Bestätigungsnummernfeld, weil der Client den Bytestrom bis einschließlich zur Sequenznummer 79 empfangen hat und jetzt auf Byte 80 und folgende wartet. Möglicherweise mutet es seltsam an, dass dieses Segment auch eine Sequenznummer hat,
obwohl es keine Daten enthält. Da TCP aber ein Sequenznummernfeld beinhaltet,
muss das Segment irgendeine Sequenznummer haben.
3.5.5 Zuverlässiger Datentransfer
Wie wir wissen, ist der Dienst auf der Internet-Vermittlungsschicht (IP-Dienst) unzuverlässig. IP sichert keine Übertragung von Datagrammen und auch keine geordnete
Übertragung von Datagrammen und keine Integrität der Daten in den Datagrammen
zu. Im IP-Dienst können Datagramme Router-Puffer zum Überlauf bringen und nie
ihr Ziel erreichen, Datagramme können außer der Reihe ankommen und Bits im
Datagramm können beschädigt (von 0 auf 1, und umgekehrt, umgedreht) werden. Da
Segmente der Transportschicht mittels IP-Datagrammen in einem Netzwerk übertragen werden, können die Segmente der Transportschicht auch an diesen Problemen
leiden.
Kapitel 3 – Transportschicht
220
TCP setzt einen zuverlässigen Datentransferdienst auf den unzuverlässigen BestEffort-Dienst von IP auf. Der zuverlässige Datentransferdienst von TCP stellt sicher,
dass der Datenstrom, den ein Prozess aus seinem TCP-Empfangspuffer liest, nicht
beschädigt ist, keine Lücken hat, nicht dupliziert wurde und in der richtigen Sequenz
vorliegt, d. h. mit dem vom Endsystem auf der anderen Seite der Verbindung gesendeten Bytestrom identisch ist. In diesem Abschnitt bieten wir eine Übersicht über die
Bereitstellung eines zuverlässigen Datentransfers durch TCP. Wir werden sehen, dass
der zuverlässige Datentransferdienst von TCP viele Prinzipien nutzt, die in Abschnitt
3.4 beschrieben wurden.
Abbildung 3.31 zeigt die drei wichtigsten Ereignisse in Zusammenhang mit der
Datenübertragung/Neuübertragung bei einem vereinfachten TCP-Sender. Wir gehen
von einer TCP-Verbindung zwischen Host A und B aus und konzentrieren uns auf
den Datenstrom, der von Host A zu Host B gesendet wird. Auf dem sendenden Host
(A) werden Anwendungsdaten an TCP weitergegeben, das diese in Segmente rahmt
und sie dann an IP weitergibt. Die Weitergabe von Daten von der Anwendung an
TCP und die anschließende Rahmung und Übertragung eines Segments ist das erste
wichtige Ereignis, das der TCP-Sender bewältigen muss. Jedes Mal, wenn TCP ein
Segment an IP freigibt, startet es einen Timer für dieses Segment. Wenn dieser Timer
abläuft, wird ein Interrupt-Ereignis in Host A erzeugt. TCP antwortet auf das Timeout-Ereignis – das zweite wichtige Ereignis, das der TCP-Sender behandeln muss –
dadurch, dass es das Segment, das das Timeout verursacht hat, erneut überträgt.
Abbildung 3.31 Vereinfachter TCP-Sender
/*Angenommen, der Sender ist nicht durch die Fluss- oder Überlastkontrolle
von TCP eingeschränkt, die Daten von oben sind größenmäßig kleiner als
die MSS, und der Datenverkehr fließt nur in eine Richtung.
*/
sendbase=initial_sequence number /*siehe Abbildung 3.18*/
nextseqnum=initial_sequence number
loop (forever) {
switch
event:
(event)
data received from application above
create TCP segment with sequence number
nextseqnum
start timer for segment nextseqnum
pass segment to IP
nextseqnum=nextseqnum+length(data)
break; /* Ende des Ereignisses “Daten von
oben empfangen“ */
➔
3.5 Verbindungsorientierter Transport: TCP
➔
event:
timer timeout for segment with sequence
number y
retransmit segment with sequence number y
compute new timeout interval for segment y
restart timer for sequence number y
break; /* Ende des Timeout-Ereignisses */
event:
ACK received, with ACK field value of y
if (y > sendbase) {/* kumulative Bestätigung aller Daten
bis y */
cancel all timers for segments with sequence numbers < y
sendbase=y
}
else { /* Duplikatbestätigung für bereits bestätigtes
Segment */
increment number of duplicate ACKs received for y
if (number of duplicate ACKs received for y==3) {
/* TCP Fast-Retransmit */
resend segment with sequence number y
restart timer for segment y
}
break; /* Ende des Ereignisses “ACK empfangen“ */
} /* Ende der Endlos-Schleife */
Das dritte wichtige Ereignis, das der TCP-Sender behandeln muss, ist die Ankunft
eines Bestätigungssegments (ACK) vom Empfänger (genauer, ein Segment, das im
ACK-Feld einen gültigen Wert enthält). Hier muss das senderseitige TCP ermitteln,
ob das ACK ein erstmaliges ACK für ein Segment, für das der Sender noch eine
Bestätigung erhalten muss, oder ein so genanntes Duplikat-ACK ist, das ein Segment, für das der Sender bereits eine Bestätigung erhalten hat, nochmals bestätigt. Im
Fall der Ankunft eines erstmaligen ACK weiß der Sender jetzt, dass alle Daten bis zu
dem Byte, das bestätigt wird, korrekt beim Empfänger angekommen sind. Der Sender kann somit seine TCP-Zustandsvariable, welche die Sequenznummer des letzten
Bytes, das bekanntlich korrekt und in der richtigen Reihenfolge beim Empfänger
angekommen ist, aktualisieren.
221
Kapitel 3 – Transportschicht
222
PRINZIPIEN IN DER PRAXIS
TCP bietet zuverlässigen Datenverkehr durch Verwendung positiver Bestätigungen und
Timer mehr oder weniger auf die Art, die wir in Abschnitt 3.4 beschrieben haben. TCP
bestätigt Daten, die korrekt empfangen wurden, und sendet Segmente erneut, wenn
diese oder ihre jeweiligen Bestätigungen für verloren oder beschädigt gehalten werden.
Bestimmte Versionen von TCP verfügen auch über einen impliziten NAK-Mechanismus. In
Verbindung mit dem Fast-Retransmit-Mechanismus von TCP dient der Empfang dreier
Duplikat-ACKs für ein bestimmtes Segment als implizites NAK für das anschließende Segment, wodurch die Neuübertragung dieses Segments vor dem Timeout ausgelöst wird.
TCP verwendet Sequenznummern, damit der Empfänger verlorene oder duplizierte Segmente erkennen kann. Genauso wie im Fall unseres zuverlässigen Datentransferprotokolls, rdt3.0, kann TCP selbst nicht mit Sicherheit feststellen, ob ein Segment oder dessen ACK verloren gegangen ist, beschädigt wurde oder sich übermäßig verzögert hat.
Beim Sender ist die TCP-Reaktion in jedem Fall gleich: Neuübertragung des fraglichen
Segments.
TCP wendet auch Pipelining an, so dass mehrere übertragene, aber noch nicht bestätigte
Segmente zu irgendeinem Zeitpunkt beim Sender vorliegen können. Wir haben an früherer Stelle gesehen, dass sich der Durchsatz einer Sitzung durch Pipelining erheblich verbessern lässt, wenn das Verhältnis der Segmentgröße zur Roundtrip-Verzögerung klein ist.
Die spezifische Anzahl anstehender unbestätigter Segmente beim Sender wird durch die
Fluss- und Überlastkontrolle von TCP bestimmt. Die TCP-Flusskontrolle wird am Ende dieses Abschnitts und die TCP-Überlastkontrolle in Abschnitt 3.7 beschrieben. Vorläufig nehmen wir einfach zur Kenntnis, dass der TCP-Sender Pipelining nutzt.
Tabelle 3.1 TCP-Empfehlungen für die ACK-Erzeugung [RFC 1122, RFC 2581]
Ereignis
Aktion des TCP-Empfängers
Ankunft eines Segments in der richtigen Reihenfolge mit der erwarteten Sequenznummer. Alle Daten bis zur erwarteten
Sequenznummer bereits bestätigt. Keine
Lücken in den empfangenen Daten.
Verzögertes ACK; maximal 500 ms auf Ankunft eines weiteren Segments in der richtigen Reihenfolge warten. ACK senden, falls
das nächste richtig nummerierte Segment
innerhalb dieses Intervalls nicht ankommt.
Ankunft eines Segments in der richtigen Reihenfolge mit der erwarteten Sequenznummer. Ein weiteres Segment in der richtigen
Reihenfolge wartet auf eine ACK-Übertragung. Keine Lücken in den empfangenen
Daten.
Sofort einzelnes kumulatives ACK senden,
um beide Segmente mit der richtigen Reihenfolge zu bestätigen.
Ankunft eines Segments außer der Reihe
mit einer höheren als der erwarteten Sequenznummer. Lücke erkannt.
Sofort Duplikat-ACK mit Angabe der Sequenznummer des nächsten erwarteten
Bytes senden.
Ankunft eines Segments, das die Lücke in
den empfangenen Daten teilweise oder vollständig füllt.
Sofort ACK senden, sofern dieses Segment
mit dem unteren Ende der Lücke beginnt.
3.5 Verbindungsorientierter Transport: TCP
Um die Reaktion des Senders auf ein Duplikat-ACK zu verstehen, muss man sich
zuerst überlegen, warum der Empfänger überhaupt ein Duplikat-ACK sendet.
Tabelle 3.1 enthält eine Übersicht über die TCP-Regeln für die ACK-Erzeugung durch
Empfänger. Wenn ein TCP-Empfänger ein Segment mit einer Sequenznummer erhält,
die größer als die nächste erwartete Sequenznummer in der richtigen Reihenfolge ist,
erkennt er eine Lücke im Datenstrom, d. h. ein fehlendes Segment. Da TCP keine
negativen Bestätigungen verwendet, kann der Empfänger keine explizite negative
Bestätigung an den Sender zurückschicken. Vielmehr bestätigt er einfach das zuletzt
in der richtigen Reihenfolge angekommene Datenbyte erneut (d. h., er erzeugt ein
Duplikat-ACK dafür). Wenn der TCP-Sender drei Duplikat-ACKs für die gleichen
Daten empfängt, geht er davon aus, dass das Segment, das dem dreimal bestätigten
Segment folgt, verloren gegangen ist. In diesem Fall führt TCP ein Fast Retransmit
[RFC 2581] durch, überträgt das fehlende Segment also schleunigst noch einmal,
bevor der Timer dieses Segments abläuft.
Einige interessante Szenarien
Wir beenden diese Diskussion mit einem Blick auf ein paar einfache Szenarien. Bei
dem Szenario in Abbildung 3.32 sendet Host A ein Segment an Host B. Es sei gegeben, dass dieses Segment die Sequenznummer 92 hat und 8 Datenbyte enthält. Nach
dem Senden dieses Segments wartet Host A auf ein Segment von B mit der BestätiAbbildung 3.32 Neuübertragung aufgrund einer verlorenen Bestätigung
Host A
Host B
Timeout
Seq=92
, 8 Da
tenbyt
e
0
ACK=10
X
Verlust
Seq=92
, 8 Da
tenbyt
e
0
ACK=10
Zeit
223
224
Kapitel 3 – Transportschicht
gungsnummer 100. Das Segment von A kommt bei B an, die Bestätigung von B nach
A geht aber verloren. In diesem Fall läuft der Timer ab und Host A überträgt das gleiche Segment erneut. Wenn Host B die Neuübertragung empfängt, stellt er natürlich
anhand der Sequenznummer fest, dass das Segment Daten enthält, die er bereits
empfangen hat. Folglich verwirft TCP in Host B die Bytes im neu übertragenen Segment.
Beim zweiten Szenario sendet Host A zwei aufeinander folgende Segmente. Das
erste Segment hat die Sequenznummer 92 und 8 Datenbyte und das zweite die
Sequenznummer 100 und 20 Datenbyte. Es sei gegeben, dass beide Segmente intakt
bei B ankommen und B zwei getrennte Bestätigungen für jedes dieser Segmente sendet. Die erste dieser Bestätigungen hat die Bestätigungsnummer 100 und die zweite
120. Wir nehmen weiter an, dass keine der Bestätigungen bei Host A vor dem Timeout des ersten Segments ankommt. Wenn der Timer abläuft, sendet Host A das erste
Segment mit Sequenznummer 92 noch einmal. Nun stellt sich die Frage, ob A auch
das zweite Segment erneut sendet? Laut den oben beschriebenen Regeln sendet Host
A das Segment nur noch einmal, wenn der Timer abläuft, bevor eine Bestätigung mit
einer Bestätigungsnummer von 120 oder höher ankommt. Falls die zweite Bestätigung nicht verloren geht und vor dem Timeout des zweiten Segments ankommt, sendet A das zweite Segment nicht noch einmal (siehe Abbildung 3.33).
Beim dritten und letzten Szenario nehmen wir an, dass Host A – wie im zweiten
Beispiel – zwei Segmente sendet. Die Bestätigung des ersten Segments geht im Netzwerk verloren, doch genau vor dem Timeout des ersten Segments empfängt Host A
eine Bestätigung mit Bestätigungsnummer 120. Host A weiß also, dass Host B alles bis
Byte 119 empfangen hat. Host A sendet demnach keines der beiden Segmente noch
einmal. Dieses Szenario ist in Abbildung 3.34 dargestellt.
Wir wissen aus dem vorherigen Abschnitt, dass TCP ein Protokoll im Stil GoBack-N ist. Das ist deshalb so, weil Bestätigungen kumulativ sind und korrekt empfangene, jedoch außer der Reihe ankommende Segmente nicht einzeln vom Empfänger bestätigt werden. Wie in Abbildung 3.31 dargestellt (siehe hierzu auch Abbildung
3.18), muss der TCP-Sender also nur die kleinste Sequenznummer eines übertragenen, aber unbestätigten Bytes (sendbase) und die Sequenznummer des als Nächstes
zu sendenden Bytes (nextseqnum) verfolgen. Man beachte aber, dass TCP ungeachtet
der Ähnlichkeit seiner zuverlässigen Datentransferkomponente mit Go-Back-N keinesfalls eine reine Implementierung von Go-Back-N ist. Um einige wichtige Unterschiede zwischen TCP und Go-Back-N herauszustellen, betrachte man, was passiert,
wenn der Sender eine Sequenz von Segmenten 1, 2, …, N sendet und alle Segmente in
der richtigen Reihenfolge ohne Fehler beim Empfänger ankommen. Weiter nehmen
wir an, dass die Bestätigung für Paket n < N verloren geht, die übrigen N – 1 Bestätigungen beim Sender aber vor dem jeweiligen Timeout ankommen. Bei diesem Beispiel würde Go-Back-N nicht nur Paket n, sondern auch alle nachfolgenden Pakete
n + 1, n + 2, …, N erneut übertragen. TCP würde demgegenüber höchstens ein Segment, nämlich Segment n, noch einmal übertragen. Außerdem würde TCP nicht einmal Segment n erneut übertragen, wenn die Bestätigung für Segment n + 1 vor dem
Timeout für Segment n ankäme.
In mehreren neueren Vorschlägen [RFC 2018; Fall 1996; Mathis 1996] für die
Erweiterung des Bestätigungsschemas von TCP wird stärker auf ein SelectiveRepeat-Protokoll gesetzt. Die Kernidee in diesen Vorschlägen ist die Bereitstellung
expliziter Informationen für den Sender darüber, welche Segmente korrekt empfangen wurden und welche beim Empfänger noch fehlen.
3.5 Verbindungsorientierter Transport: TCP
Abbildung 3.33 Segment wird nicht erneut übertragen, weil seine Bestätigung vor dem
Timeout ankommt.
Host B
Seq=92
, 8 Da
tenbyt
e
Seq=10
0, 20
Datenb
yte
seq=92 Timeout
seq=100 Timeout
Host A
00
=1
K
AC 120
K=
AC
Seq
=92
, 8
Dat
enb
yte
0
ACK=12
Zeit
3.5.6 Flusskontrolle
Wir erinnern uns, dass die Hosts auf beiden Seiten einer TCP-Verbindung jeweils
einen Empfangspuffer für die Verbindung vorhalten. Wenn die TCP-Verbindung
Bytes korrekt und in der richtigen Sequenz empfängt, werden die Daten im Empfangspuffer abgestellt. Der damit verbundene Anwendungsprozess liest die Daten
aus diesem Puffer, aber nicht unbedingt in dem Augenblick, in dem sie ankommen.
Möglicherweise ist die empfangende Anwendung mit einer anderen Aufgabe
beschäftigt und versucht erst lange Zeit nach Ankunft der Daten, diese zu lesen.
Wenn die Anwendung die Daten relativ langsam liest, kann der Sender sehr leicht
den Empfangspuffer der Verbindung zum Überlauf bringen, falls er zu viele Daten zu
schnell sendet. TCP bietet seinen Anwendungen deshalb eine Flusskontrolle, um die
Möglichkeit auszuschließen, dass der Sender den Puffer des Empfängers überschwemmt. Flusskontrolle ist folglich ein Dienst zur Abstimmung der Geschwindigkeit bzw. Anpassung der Rate, in der der Sender überträgt, auf die Rate, in der die
empfangende Anwendung liest. Wie bereits erwähnt, kann ein TCP-Sender auch aufgrund einer Überlast im IP-Netzwerk gedrosselt werden. Diese Form der Senderkontrolle wird als Überlastkontrolle bezeichnet (siehe Abschnitte 3.6 und 3.7). Obwohl
225
Kapitel 3 – Transportschicht
226
Abbildung 3.34 Durch eine kumulative Bestätigung wird die Neuübertragung des ersten
Segments vermieden.
Seq=9
2, 8
Daten
byte
00
ACK=1
Seq=1
00,
X
20 Da
tenby
te
20
ACK=1
Zeit
Zeit
sich die von der Fluss- und Überlaufkontrolle unternommenen Aktionen (Drosseln
des Senders) ähneln, dienen sie natürlich unterschiedlichen Zwekken. Leider verwenden viele Autoren die beiden Begriffe gleichbedeutend, so dass nur der sachkundige Leser mit viel Sorgfalt die beiden Fälle aus dem Zusammenhang heraus unterscheiden kann. Wir beschreiben im Folgenden zuerst die von TCP bereitgestellte
Flusskontrolle.
TCP stellt Flusskontrolle dadurch bereit, dass es den Sender eine Variable verwalten lässt, die man als Empfangsfenster (Receive Window) bezeichnet. Informell wird
das Empfangsfenster benutzt, um dem Sender eine Vorstellung davon zu vermitteln,
wie viel freier Pufferplatz beim Empfänger zur Verfügung steht. In einer Vollduplexverbindung verwaltet der Sender auf jeder Seite der Verbindung ein eigenes Empfangsfenster. Das Empfangsfenster ist dynamisch, d. h., es ändert sich im Verlauf
einer Verbindung. Wir untersuchen das Empfangsfenster in Zusammenhang mit
einem Filetransfer als Beispiel. Angenommen, Host A sendet eine große Datei über
eine TCP-Verbindung an Host B. Host B weist dieser Verbindung einen Empfangspuffer zu; dessen Größe sei durch RcvBuffer gegeben. Von Zeit zu Zeit liest
der Anwendungsprozess in Host B aus dem Puffer. Wir definieren die folgenden
Variablen:
3.5 Verbindungsorientierter Transport: TCP
227
õ LastByteRead = Nummer des letzten Bytes im Datenstrom, das von dem Anwendungsprozess in B aus dem Puffer gelesen wird.
õ LastByteRcvd = Nummer des letzten Bytes im Datenstrom, das vom Netzwerk
angekommen ist und in den Empfangspuffer bei B gestellt wurde.
Da es TCP nicht gestattet ist, den zugeteilten Puffer zum Überlauf zu bringen, benötigen wir:
LastByteRcvd – LastByteRead <= RcvBuffer
Das als RcvWindow bezeichnete Empfangsfenster wird auf die Menge des freien Platzes im Puffer gesetzt:
RcvWindow = RcvBuffer – [LastByteRcvd – LastByteRead]
Da sich der freie Platz im Verlauf der Zeit ändert, ist RcvWindow dynamisch. Die Variable RcvWindow ist in Abbildung 3.35 dargestellt.
Abbildung 3.35 Das Empfangsfenster (RcvWindow) und der Empfangspuffer
(RcvBuffer)
RcvWindow
Daten von
IP
Freier Platz
TCPDaten
im Puffer
Anwendungsprozess
RcvBuffer
Wie benutzt die Verbindung die Variable RcvWindow, um den Flusskontrolldienst
bereitzustellen? Host B teilt Host A mit, wie viel freier Platz sich im Verbindungspuffer befindet, indem er seinen aktuellen Wert von RcvWindow in das Fensterfeld jedes
an A gesendeten Segments einfügt. Anfangs setzt Host B RcvWindow = RcvBuffer.
Damit dies gelingt, muss Host B natürlich mehrere verbindungsspezifische Variablen
verfolgen.
Host A verwaltet seinerseits die beiden Variablen LastByteSent (letztes gesendetes Byte) und LastByteAcked (letztes bestätigtes Byte). Der Unterschied zwischen diesen beiden Variablen ist die Menge der unbestätigten Daten, die A in die Verbindung
gespeist hat. Dadurch, dass die Menge der unbestätigten Daten unter dem Wert von
RcvWindow gehalten wird, kann Host A sicher sein, dass er den Empfangspuffer in
Host B nicht zum Überlaufen bringt. Somit stellt Host A während der gesamten
Lebensdauer der Verbindung Folgendes sicher:
LastByteSent – LastByteAcked ≤ RcvWindow
228
Kapitel 3 – Transportschicht
Bei diesem Schema ergibt sich ein kleines technisches Problem. Um es zu verdeutlichen, nehmen wir an, der Empfangspuffer von Host B füllt sich, so dass RcvWindow
= 0. Nach dem Advertising von RcvWindow = 0 an Host A gehen wir ferner davon
aus, dass B nichts an A zu senden hat. Während der Anwendungsprozess in B den
Puffer leert, sendet TCP keine neuen Segmente mit neuen Empfangsfenstern an Host
A. Das heißt, TCP sendet nur dann ein Segment an Host A, wenn Daten zum Senden
anstehen oder wenn es eine Bestätigung senden muss. Deshalb wird Host A nie darüber informiert, dass im Empfangspuffer von Host B wieder Platz freigeworden ist:
Host A ist blockiert und kann keine Daten mehr übertragen! Um dieses Problem zu
lösen, verlangt die TCP-Spezifikation von Host A, das Senden von Segmenten mit
jeweils einem Datenbyte fortzusetzen, wenn das Empfangsfenster von B Null ist.
Diese Segmente werden vom Empfänger bestätigt. Letztendlich beginnt sich der Puffer zu leeren und die Bestätigungen werden einen RcvWindow-Wert von nicht Null enthalten.
Nach der Beschreibung der TCP-Flusskontrolle sei an dieser Stelle kurz erwähnt,
dass UDP keine Flusskontrolle bietet. Um den Unterschied hier hervorzuheben,
betrachte man das Senden einer Reihe von UDP-Segmenten von einem Prozess in
Host A an einen Prozess in Host B. Bei einer typischen UDP-Implementierung hängt
UDP die Segmente (bzw. genauer gesagt, die Daten in den Segmenten) an eine Warteschlange mit endlicher Größe an, die dem entsprechenden Socket (der Tür zum Prozess) »vorangestellt« ist. Der Prozess liest jeweils ein ganzes Segment aus der Warteschlange. Wenn der Prozess die Segmente nicht schnell genug aus der Warteschlange
liest, läuft die Warteschlange über und Segmente gehen verloren.
Zu diesem Abschnitt bieten wir (in der Online-Version dieses Buchs) ein interaktives Java-Applet, das wichtige Einblicke in TCP-Empfangsfenster gewährt.
3.5.7 Roundtrip-Zeit und Timeout
Wenn ein Host ein Segment in eine TCP-Verbindung einspeist, startet er einen Timer.
Läuft der Timer ab, bevor der Host eine Bestätigung für die im Segment gesendeten
Daten empfängt, überträgt der Host das Segment erneut. Die Zeit vom Start bis zum
Ablauf des Timers bezeichnet man als Timeout. Eine natürliche Frage ist, wie groß
das Timeout sein soll. Selbstverständlich sollte es größer als die Roundtrip-Zeit der
Verbindung sein, d. h. die Zeit zwischen dem Senden eines Segments und seiner
Bestätigung. Andernfalls würden unnötige Neuübertragungen gesendet werden. Das
Timeout sollte andererseits aber nicht viel größer als die Roundtrip-Zeit sein, sonst
würde TCP beim Verlust eines Segments dieses nicht schnell genug erneut übertragen und dadurch beträchtliche Datentransferverzögerungen in die Anwendung einführen. Bevor wir das Timeout-Intervall ausführlicher beschreiben, befassen wir uns
eingehender mit der Roundtrip-Zeit (RTT). Die folgende Beschreibung basiert auf
einer TCP-Arbeit in [Jacobson 1988].
Abschätzung der durchschnittlichen Roundtrip-Zeit
Die als SampleRTT bezeichnete Muster-RTT für ein Segment ist die Zeit zwischen
dem Senden des Segments (d. h. Weitergabe an IP) und dem Empfang einer Bestätigung für das Segment. Jedes gesendete Segment hat seine eigene SampleRTT. Natürlich schwanken die SampleRTT-Werte je nach Überlast in den Routern und verschiedenen Lasten in den Endsystemen von einem Segment zum anderen. Aufgrund
dieser Schwankungen kann ein gegebener SampleRTT-Wert untypisch sein. Um eine
3.5 Verbindungsorientierter Transport: TCP
typische RTT zu schätzen, ist es deshalb ganz natürlich, die eine oder andere Art von
Durchschnitt der SampleRTT-Werte heranzuziehen. TCP verwendet eine Durchschnittszeit, EstimatedRTT, der SampleRTT-Werte. Beim Empfang einer Bestätigung
und Erhalt einer neuen SampleRTT aktualisiert TCP die EstimatedRTT gemäß folgender Formel:
EstimatedRTT = (1 – x) · EstimatedRTT + x · SampleRTT
Die obige Formel ist in der Form einer Anweisung für Programmiersprachen
geschrieben: Der neue Wert von EstimatedRTT ist eine gewichtete Kombination des
vorherigen Werts von EstimatedRTT und des neuen Werts von SampleRTT. Ein typischer Wert von x ist x = 0,125 (d. h. 1/8); in diesem Fall lautet die obige Formel:
EstimatedRTT = 0,875 EstimatedRTT + 0,125 · SampleRTT
Man beachte, dass EstimatedRTT einen gewichteten Durchschnitt der SampleRTTWerte darstellt. Wie wir in den Wiederholungsfragen am Ende des Kapitels sehen
werden, legt dieser gewichtete Durchschnitt mehr Gewicht auf neuere statt ältere
Muster. Das ist ganz natürlich, denn die neueren Muster spiegeln die aktuelle Überlast im Netzwerk besser wider. In der Statistik wird ein solcher Durchschnitt als
Exponential Weighted Moving Average (EWMA) bezeichnet. Das Wort »exponentiell« erscheint in EWMA, weil das Gewicht einer bestimmten SampleRTT exponentiell schneller verfällt, als die Aktualisierungen erfolgen. In den Übungen am Ende
dieses Kapitels werden Sie gebeten, den exponentiellen Term in EstimatedRTT abzuleiten.
Abbildung 3.36 zeigt die SampleRTT-Werte (gepunktete Linie) und die EstimatedRTT (durchgezogene Linie) für einen Wert von x = 1/8 für eine TCP-Verbindung zwischen void.cs.umass.edu (in Amherst, Massachusetts) und maria.wustl. edu (in St.
Louis, Missouri). Die Schwankungen in der SampleRTT wurden in der Berechnung der
EstimatedRTT geglättet.
Abbildung 3.36 RTT-Muster (Samples) und RTT-Durchschnitt (Average)
229
Kapitel 3 – Transportschicht
230
Setzen eines Timeout
Das Timeout sollte so gesetzt werden, dass ein Timer nur in seltenen Fällen früh vor
der verzögerten Ankunft der Bestätigung eines Segments abläuft. Naturgemäß setzt
man das Timeout gleich der EstimatedRTT zuzüglich eines gewissen Toleranzspielraums. Der Spielraum sollte ausreichend groß sein, wenn mit starker Fluktuation in
den SampleRTT-Werten zu rechnen ist. Andererseits sollte er bei geringer Fluktuation
klein sein. TCP verwendet die folgende Formel:
Timeout = EstimatedRTT + 4·Abweichung
wobei Abweichung eine Schätzung dessen ist, um wie viel SampleRTT normalerweise
von EstimatedRTT abweicht:
Abweichung = (1 – x) ⋅ Abweichung + x ⋅ |SampleRTT – EstimatedRTT|
Man beachte, dass Abweichung ein EWMA dessen ist, um wie viel SampleRTT von
EstimatedRTT abweicht. Wenn die SampleRTT-Werte wenig Fluktuation aufweisen,
dann ist Abweichung klein und Timeout kaum größer als EstimatedRTT. Ist die Fluktuation dagegen groß, ist Abweichung groß und Timeout viel größer als EstimatedRTT. »A
Quick Tour around TCP« [Cela 2000] enthält hervorragende interaktive Applets für
Schätzungen der RTT-Varianz.
3.5.8 TCP-Verbindungsmanagement
In diesem Abschnitt beschreiben wir ausführlich den Auf- und Abbau einer TCP-Verbindung. Das Thema mag zwar nicht sonderlich aufregend erscheinen, ist aber wichtig, weil der Aufbau einer TCP-Verbindung die wahrgenommenen Verzögerungen
(z. B. beim Surfen im Web) deutlich erhöhen kann. Wir nehmen als Beispiel an, dass
ein Prozess in einem Host (Client) eine Verbindung zu einem anderen Prozess in
einem anderen Host (Server) aufbauen möchte. Der Anwendungsprozess im Client
informiert zuerst das Client-TCP, dass er eine Verbindung zu einem Prozess im Server
aufbauen möchte. Das TCP im Client fährt dann mit dem Aufbau einer TCP-Verbindung zum TCP im Server wie folgt fort:
õ Schritt 1: Das clientseitige TCP sendet zuerst ein spezielles TCP-Segment an das
serverseitige TCP. Dieses spezielle Segment enthält keine Anwendungsdaten.
Eines der Flag-Bits im Segment-Header (siehe Abbildung 3.28), das so genannte
»SYN-Bit«, ist aber auf 1 gesetzt. Aus diesem Grund gilt dieses spezielle Segment
als SYN-Segment. Darüber hinaus wählt der Client eine anfängliche Sequenznummer (client_isn) und setzt diese Nummer im Sequenznummernfeld auf das
anfängliche SYN-Segment. Dieses Segment wird in einem IP-Datagramm verkapselt und an den Server gesendet.
õ Schritt 2: Nachdem das IP-Datagramm mit dem SYN-Segment beim Server-Host
ankommt (wir gehen einfach einmal davon aus!), extrahiert der Server das SYNSegment aus dem Datagramm, weist der Verbindung TCP-Puffer und Variablen
zu und sendet ein Verbindung-gewährt-Segment an das Client-TCP. Dieses Verbindung-gewährt-Segment enthält ebenfalls keine Anwendungsdaten, dafür aber
drei wichtige Informationen im Segment-Header. Erstens ist das SYN-Bit auf 1
gesetzt. Zweitens ist das Bestätigungsfeld auf client_isn+1 gesetzt. Und drittens
wählt der Server seine eigene Anfangssequenznummer (server_isn) und setzt
diesen Wert in das Sequenznummernfeld des Segment-Headers. Dieses Verbindung-gewährt-Segment besagt: »Ich habe dein SYN-Paket empfangen, um eine
3.5 Verbindungsorientierter Transport: TCP
231
Verbindung mit deiner Anfangssequenznummer, client_isn, zu starten. Ich bin
einverstanden, diese Verbindung aufzubauen. Meine eigene Anfangssequenznummer lautet server_isn.« Das Verbindung-gewährt-Segment wird als SYNACK-Segment bezeichnet.
õ Schritt 3: Beim Empfang des SYNACK-Segments weist der Client seinerseits der
Verbindung Puffer und Variablen zu. Anschließend sendet der Client-Host dem
Server noch ein Segment, mit dem er das SYNACK-Segment des Servers bestätigt
(hierfür setzt der Client den Wert server_isn+1 in das Bestätigungsfeld des TCPSegment-Headers). Das SYN-Bit wird auf 0 gesetzt, weil die Verbindung inzwischen steht.
Sind diese Schritte vollzogen, können der Client- und der Server-Host einander Segmente mit Daten zusenden. In jedem dieser Segmente wird das SYN-Bit auf Null
gesetzt. Für den Aufbau der Verbindung werden also drei Pakete zwischen zwei
Hosts gesendet (siehe Abbildung 3.37). Aus diesem Grund wird dieser Verbindungsaufbau als Drei-Wege-Handshake bezeichnet. In den Übungen werden mehrere
Aspekte des Drei-Wege-Handshake von TCP untersucht. (Warum sind Anfangssequenznummern erforderlich? Warum genügt kein Zwei-Wege-Handshake?)
Abbildung 3.37 Segmentaustausch im Drei-Wege-Handshake von TCP
Client-Host
Server-Host
Verbindu
ngsanfra
ge(SYN=1,
seq=client
_isn)
sn,
i
_
r
e
erv
seq=s
YN=1,
S
(
t
r
+1)
h
_isn
g ewä
ient
ung
l
d
c
n
=
i
a ck
Verb
ACK(S
YN=0,
seq=c
lient
ack=s
_isn+
erver
1)
_isn+
1)
Zeit
Zeit
Was lange währt, muss schließlich auch einmal zu einem Ende kommen, und so ist es
auch mit einer TCP-Verbindung. Beide an einer TCP-Verbindung teilnehmenden Prozesse können die Verbindung beenden. Bei Beendigung einer Verbindung wird die
Zuweisung der »Ressourcen« (Puffer und Variablen) in den Hosts wieder aufgehoben. Als Beispiel nehmen wir an, dass der Client die Verbindung schließen möchte,
wie in Abbildung 3.38 dargestellt. Der Anwendungsprozess des Clients gibt einen
Kapitel 3 – Transportschicht
Close-Befehl aus. Dies veranlasst das Client-TCP, ein spezielles TCP-Segment an den
Server-Prozess zu senden. In diesem speziellen Segment ist das FIN-Bit im SegmentHeader (siehe Abbildung 3.38) auf 1 gesetzt. Empfängt der Server dieses Segment,
sendet er dem Client ein Bestätigungssegment. Anschließend sendet der Server sein
eigenes Shutdown-Segment, in dem das FIN-Bit auf 1 gesetzt ist. Schließlich bestätigt
der Client das Shutdown-Segment des Servers. An diesem Punkt werden sämtliche
zugewiesenen Ressourcen in den beiden Hosts wieder freigegeben.
Abbildung 3.38 Schließen einer TCP-Verbindung
Client
Close
Server
FIN
ACK
FIN
Close
ACK
Wartezeit
232
geschlossen
Zeit
Während der Lebensdauer einer TCP-Verbindung geht das TCP-Protokoll in jedem
der beiden Hosts in verschiedene TCP-Zustände über. Abbildung 3.39 zeigt eine
typische Abfolge von TCP-Zuständen, die vom Client-TCP durchschritten werden.
Das Client-TCP beginnt im Zustand CLOSED. Die Anwendung auf der Client-Seite leitet eine neue TCP-Verbindung (durch Erzeugen eines Socket-Objekts, wie in unseren
Java-Beispielen in Kapitel 2) ein. Dies veranlasst das TCP im Client, ein SYN-Segment an das TCP im Server zu senden. Anschließend geht das Client-TCP in den
Zustand SYN_SENT über. Es wartet auf ein Segment vom Server-TCP, das eine Bestätigung für das vorherige Segment des Clients mit dem auf 1 gesetzten SYN-Bit enthält. Nach dem Empfang eines solchen Segments geht das Client-TCP in den
Zustand ESTABLISHED über. In diesem Zustand kann der TCP-Client TCP-Segmente
senden und empfangen, die Nutzdaten (d. h. von der Anwendung erzeugte Daten)
enthalten.
Angenommen, die Client-Anwendung möchte die Verbindung beenden. (Ebenso
könnte das aber auch der Server sein.) Dies veranlasst das Client-TCP zum Versenden
eines TCP-Segments, in dem das FIN-Bit auf 1 gesetzt ist, und zum Übergang in den
3.5 Verbindungsorientierter Transport: TCP
Abbildung 3.39 Typische Abfolge von TCP-Zuständen eines TCP-Clients
Client-Anwendung leitet
Verbindungsaufbau ein
30 Sekunden warten
CLOSED
SYN senden
TIME_WAIT
SYN_SENT
SYN und ACK empfangen,
ACK senden
FIN empfangen,
ACK senden
FIN_WAIT_2
ESTABLISHED
FIN senden
ACK empfangen,
nichts senden
FIN_WAIT_1
Client-Anwendung leitet
Verbindungsabbau ein
Zustand FIN_WAIT_1. In diesem Zustand wartet das Client-TCP auf ein TCP-Segment
vom Server mit einer Bestätigung. Wenn das Client-TCP dieses Segment empfängt,
wechselt es in den Zustand FIN_WAIT_2. In diesem Zustand wartet der Client auf ein
weiteres Segment vom Server, in dem das FIN-Bit auf 1 gesetzt ist. Nach dem Empfang dieses Segments bestätigt das Client-TCP das Segment des Servers und wechselt
in den Zustand TIME_WAIT. In diesem Zustand sendet der TCP-Client die letzte Bestätigung noch einmal, falls das ACK verloren geht. Die Dauer des Verharrens im
Zustand TIME_WAIT hängt von der Implementierung ab; übliche Werte sind 30 Sekunden, 1 Minute und 2 Minuten. Nach Ablauf der Wartezeit wird die Verbindung formell geschlossen und alle Ressourcen auf der Client-Seite (einschließlich Portnummern) werden freigegeben.
Abbildung 3.40 zeigt den Ablauf der Zustände, die das serverseitige TCP im typischen Fall unter der Annahme durchläuft, dass der Client den Abbau der Verbindung
einleitet. Die Übergänge sind selbst erklärend. In diesen beiden Zustandsübergangsdiagrammen stellen wir nur dar, wie eine TCP-Verbindung normalerweise auf- und
abgebaut wird. Wir beschreiben nicht, was in bestimmten pathologischen Szenarien
passiert, wenn beispielsweise beide Seiten einer Verbindung gleichzeitig beenden
wollen. Wenn Sie daran interessiert sind, mehr darüber und zu weiteren Fragen in
Zusammenhang mit TCP zu erfahren, empfiehlt sich Stevens Buch [Stevens 1994].
Dies beschließt unsere Einführung in TCP. In Abschnitt 3.7 werden wir zu TCP
zurückkehren und die TCP-Kontrolle der Netzwerküberlastung eingehend behandeln. Zuvor jedoch gehen wir einen Schritt zurück und untersuchen Einzelheiten der
Kontrolle der Netzwerküberlastung in einem breiteren Kontext.
233
Kapitel 3 – Transportschicht
234
Abbildung 3.40 Typische Sequenz von TCP-Zuständen, die ein serverseitiges TCP durchläuft
ACK empfangen,
nichts senden
Server-Anwendung erzeugt
ein Listen-Socket
CLOSED
LAST_ACK
LISTEN
SYN empfangen,
SYN und ACK senden
FIN senden
CLOSE_WAIT
FIN empfangen,
ACK senden
SYN_RCVD
ESTABLISHED
ACK empfangen,
nichts senden
3.6 Grundlagen der Überlastkontrolle
In den vorherigen Abschnitten wurden die allgemeinen Prinzipien und spezifischen
TCP-Mechanismen beschrieben, mit denen TCP im Fall eines Paketverlustes einen
zuverlässigen Datentransferdienst bereitstellen kann. Wir erwähnten auch, dass ein
solcher Verlust in der Praxis durch den Überlauf von Router-Puffern in einem überlasteten Netzwerk entsteht. Die Neuübertragung von Paketen behandelt somit ein
Symptom (Verlust eines spezifischen Segments auf der Transportschicht), nicht aber
die Ursache einer Netzwerküberlast – zu viele Quellen versuchen, Daten in einer zu
hohen Rate zu senden. Für die Behandlung der Ursache einer Netzwerküberlast sind
Mechanismen erforderlich, um Sender angesichts der Netzwerküberlast zu drosseln.
Dieser Abschnitt befasst sich mit dem Problem der Überlastkontrolle in einem allgemeinen Zusammenhang, wobei wir untersuchen, warum Überlast »schlecht« ist,
wie sich Netzwerküberlast auf die Leistung für die höherschichtigen Anwendungen
auswirkt und welche Ansätze umgesetzt werden können, um Netzwerküberlast zu
vermeiden oder darauf zu reagieren. Diese eher allgemeine Untersuchung von Überlastkontrolle erscheint angemessen, weil sie beim zuverlässigen Datentransfer ganz
oben auf der »Hitliste« der grundlegenden Netzwerkprobleme steht. Wir beenden
diesen Abschnitt mit einer Diskussion der Überlastkontrolle im ABR-Dienst in ATMNetzwerken (Asynchronous Transfer Mode). Der anschließende Abschnitt befasst
sich ausführlich mit dem Überlastkontrollalgorithmus von TCP.
3.6.1 Ursachen und Kosten einer Überlast
Wir beginnen unsere allgemeine Untersuchung der Überlastkontrolle mit einer eingehenden Betrachtung dreier zunehmend komplexer Szenarien, in denen Überlast ent-
3.6 Grundlagen der Überlastkontrolle
235
steht. In jedem Fall untersuchen wir die Gründe für die Überlast und welche Kosten
(hinsichtlich nicht voll ausgelasteter Ressourcen und schlechter Leistung aus Sicht
der Endsysteme) dadurch entstehen.
Szenario 1: Zwei Sender, ein Router mit unendlichen Puffern
Dieses Szenario ist das wohl einfachste, das man sich vorstellen kann: Zwei Hosts (A
und B) verfügen jeweils über eine Verbindung, die einen einzigen Hop zwischen
Quelle und Ziel gemeinsam nutzt (siehe Abbildung 3.41).
Abbildung 3.41 Zwei Verbindungen nutzen einen einzigen Hop mit unendlichen Puffern
gemeinsam.
Host A
Host B
λin: Originaldaten
λout
λ’in: Originaldaten
Router mit
unendlichen Puffern
Angenommen, die Anwendung in Host A sendet Daten über die Verbindung (z. B.
Weiterleitung von Daten über ein Socket an das Protokoll der Transportschicht) in
einer durchschnittlichen Rate von λin Byte/s. Diese Daten sind in dem Sinn »Originale«, als jede Dateneinheit nur einmal in das Socket gespeist wird. Das zugrunde liegende Transportprotokoll ist sehr einfach. Die Daten werden verkapselt und gesendet; es wird keine Wiederherstellung nach einem Fehler (z. B. Neuübertragung),
Flusskontrolle oder Überlastkontrolle durchgeführt. Host B arbeitet auf ähnliche
Weise und wir nehmen der Einfachheit halber an, dass er ebenfalls in einer Rate von
λin Byte/s sendet. Die Pakete von Host A und B fließen weiter an einen Router und
über eine gemeinsam genutzte Ausgangsverbindung mit Kapazität R. Der Router
verfügt über Puffer, in denen ankommende Pakete gespeichert werden können, wenn
die Ankunftsrate der Pakete die Kapazität der Ausgangsverbindungsleitung übersteigt. In diesem ersten Szenario gehen wir davon aus, dass der Router einen unendlichen Pufferplatz hat.
Abbildung 3.42 zeigt die Leistung der Verbindung von Host A im ersten Szenario.
Der linke Graph stellt den per-Verbindung-Durchsatz (Anzahl Byte pro Sekunde
beim Empfänger) als Funktion der Senderate der Verbindung dar. Bei einer Senderate
zwischen 0 und R/2 ist der Durchsatz beim Empfänger gleich der Senderate des Senders; alles, was der Sender sendet, wird beim Empfänger mit einer endlichen Verzö-
Kapitel 3 – Transportschicht
236
gerung empfangen. Wenn die Senderate allerdings über R/2 liegt, beträgt der Durchsatz nur R/2. Diese Obergrenze des Durchsatzes ist eine Konsequenz der
gemeinsamen Nutzung der Leitungskapazität durch zwei Verbindungen. Die Verbindungsleitung kann einfach keine Pakete in einer Dauerrate, die R/2 übersteigt, an
einen Empfänger durchreichen. Gleichgültig, wie hoch die Rate ist, in der die Hosts A
und B senden, sie werden jeweils nie einen Durchsatz von mehr als R/2 erleben.
Abbildung 3.42 Überlastszenario 1: Durchsatz und Verzögerung als Funktion der HostSenderate
λ out
Verzögerung
R/2
λ′in
(a)
R/2
λ′in
R/2
(b)
Die Erreichung eines per-Verbindung-Durchsatzes von R/2 mag tatsächlich wie eine
»gute Sache« erscheinen, da die Verbindungsleitung mit der Übertragung von Paketen an ihre Ziele voll ausgelastet ist. Der rechte Graph in Abbildung 3.42 zeigt allerdings die Konsequenzen des Betriebs nahe der Verbindungsleitungskapazität. Je
mehr sich die Senderate R/2 (von links) nähert, desto höher liegt die durchschnittliche Verzögerung. Wenn die Senderate R/2 übersteigt, ist die durchschnittliche
Anzahl der in der Warteschlange des Routers anstehenden Pakete unbegrenzt und
die durchschnittliche Verzögerung zwischen Quelle und Ziel geht gegen unendlich
(unter der Annahme, dass die Verbindungen über eine unendliche Dauer in diesen
Senderaten laufen). Während also ein Gesamtdurchsatz von nahe R aus Sicht des
Durchsatzes ideal sein mag, ist dies aus Sicht der Verzögerung weit davon entfernt.
Sogar bei diesem (extrem) idealisierten Szenario können wir einen Kostenfaktor eines überlasteten Netzwerks feststellen: Mit zunehmender Annäherung der Ankunftsrate von Paketen an
die Verbindungsleitungskapazität erhöhen sich die Warteschlangenverzögerungen.
Szenario 2: Zwei Sender, ein Router mit endlichen Puffern
Wir modifizieren jetzt Szenario 1 in zweierlei Hinsicht (siehe Abbildung 3.43): Erstens
gehen wir davon aus, dass die Puffer in den Routern endlich sind. Zweitens nehmen
wir an, dass jede Verbindung zuverlässig ist. Wenn ein Paket, in dem ein Segment der
Transportschicht enthalten ist, beim Router verworfen wird, überträgt es der Sender
3.6 Grundlagen der Überlastkontrolle
237
irgendwann erneut. Da also Pakete erneut übertragen werden können, müssen wir
jetzt mit dem Begriff »Senderate« sorgfältiger umgehen. Insbesondere ist die Rate, in
der die Anwendung Originaldaten an das Socket sendet, als λin Byte/s gegeben. Die
Rate, in der die Transportschicht Segmente (in denen sich Originaldaten oder erneut
übertragene Daten befinden) in das Netzwerk einspeist, ist als λ′in Byte/s gegeben.
λ′in wird auch als die dem Netzwerk angebotene Last (offered load) bezeichnet.
Abbildung 3.43 Szenario 2: zwei Hosts (mit Neuübertragungen) und ein Router mit endlichen
Puffern
Host A
Host B
λin: Originaldaten
λ‘in: Original- und erneut
übertragene Daten
Host C
λout
Host D
Router mit
endlichen Puffern
Die in Szenario 2 realisierbare Leistung hängt jetzt stark davon ab, wie die Neuübertragung ausgeführt wird. Erstens betrachte man den unrealistischen Fall, dass Host A
irgendwie (magisch!) feststellen kann, ob im Router ein Puffer frei ist und folglich nur
ein Paket sendet, falls einer frei ist. In diesem Fall würde kein Verlust entstehen, λin
würde jetzt λ′in entsprechen und der Durchsatz der Verbindung wäre gleich λin. Dieser Fall ist durch die obere Kurve in Abbildung 3.44 (a) dargestellt. Aus Sicht des
Durchsatzes ist die Leistung ideal; alles, was gesendet wird, wird empfangen. Die
durchschnittliche Senderate des Hosts kann in diesem Szenario aber R/2 nicht übersteigen, weil ja davon ausgegangen wird, dass nie ein Paket verloren geht.
Man betrachte nun den etwas realistischeren Fall, bei dem der Sender nur eine
Neuübertragung durchführt, wenn mit Sicherheit feststeht, dass ein Paket verloren
gegangen ist. (Wiederum ist diese Annahme ein bisschen weit hergeholt. Allerdings
kann der sendende Host sein Timeout ausreichend hoch ansetzen, so dass praktisch
mit Sicherheit feststeht, dass ein Paket, das nicht bestätigt wird, verloren gegangen
ist.) In diesem Fall könnte die Leistung eher wie in Abbildung 3.44 (b) aussehen. Um
zu verstehen, was hier passiert, betrachte man den Fall, bei dem die angebotene Last,
λ′in (die Rate der Originaldatenübertragung zuzüglich Neuübertragungen), 0,5R entspricht. Gemäß Abbildung 3.44 (b) ist die Rate, in der Daten zur Empfängeranwendung übertragen werden, bei dieser angebotenen Last R/3. Folglich sind von den insgesamt 0,5R übertragenen Dateneinheiten (im Durchschnitt) 0,333R Byte/s
Originaldaten und 0,166R Byte/s neu übertragene Daten. Wir sehen hier einen weiteren
Kostenfaktor eines überlasteten Netzwerks: Der Sender muss Neuübertragungen durchführen, um verworfene (verlorene) Pakete aufgrund eines Pufferüberlaufs auszugleichen.
Kapitel 3 – Transportschicht
238
Abbildung 3.44 Leistung in Szenario 2
R/2
R/2
R/3
λ out
λ out
R/4
λ’in
(a)
R/2
λ’in
R/2
(b)
Schließlich betrachten wir den Fall, bei dem der Timer des Senders vorzeitig abläuft
und der Sender ein Paket, das in der Warteschlange verzögert wurde, aber nicht verloren gegangen ist, erneut überträgt. In diesem Fall kann sowohl das Originaldatenpaket als auch die Neuübertragung den Empfänger erreichen. Natürlich braucht der
Empfänger nur eine Kopie dieses Pakets, so dass er die Neuübertragung verwirft. Die
vom Router durchgeführte »Arbeit« in Bezug auf die Weiterleitung des neu übertragenen Exemplars war also »umsonst«, weil der Empfänger das Originalexemplar des
Pakets bereits erhalten hat. Der Router hätte die Übertragungskapazität der Verbindungsleitung besser für die Übertragung eines anderen Pakets genutzt. Hier stellen
wir einen weiteren Kostenfaktor eines überlasteten Netzwerks fest: Unnötige Neuübertragungen durch den Sender aufgrund von großen Verzögerungen können dazu führen, dass ein
Router seine Leitungsbandbreite für die Weiterleitung unnötiger Paketexemplare verschwendet. Die untere Kurve in Abbildung 3.44 (a) zeigt den Durchsatz im Vergleich zur
angebotenen Last, wenn jedes Paket (im Durchschnitt) zweimal vom Router weitergeleitet wird. Da jedes Paket zweimal weitergeleitet wird, ist der in Abbildung
3.44 (a) durch das Liniensegment dargestellte erreichte Durchsatz mit einem asymptotischen Wert von R/4 gegeben.
Szenario 3: Vier Sender, Router mit endlichen Puffern
und Multihop-Pfade
In unserem letzten Überlastszenario übertragen vier Hosts jeweils auf überlappenden
Pfaden mit zwei Hops Pakete (siehe Abbildung 3.45). Wir nehmen wiederum an, dass
jeder Host einen Timeout-/Neuübertragungsmechanismus anwendet, um einen
zuverlässigen Datentransferdienst zu implementieren, und dass alle Hosts den gleichen Wert von λin und alle Router-Verbindungsleitungen eine Kapazität von R Byte/s
haben.
Es sei gegeben, dass die Verbindung von Host A zu Host C durch die Router R1
und R2 führt. Die A-C-Verbindung teilt sich Router R1 mit der D-B-Verbindung und
Router R2 mit der B-D-Verbindung. Bei extrem kleinen Werten von λin sind Pufferüberläufe selten (wie in den Überlastszenarien 1 und 2) und der Durchsatz entspricht
3.6 Grundlagen der Überlastkontrolle
239
Abbildung 3.45 Szenario 3: Vier Sender, Router mit endlichen Puffern und Multihop-Pfade
Host A
Host B
λ in
R1
Host D
R4
R3
R2
Host C
ungefähr der angebotenen Last. Bei geringfügig größeren Werten von λin ist auch der
Durchsatz entsprechend größer, weil mehr Originaldaten über das Netzwerk zum
Ziel übertragen werden und Überläufe immer noch selten sind. Folglich führt bei
kleinen Werten von λin eine Erhöhung von λin zu einer Erhöhung von λout.
Nach diesem Fall mit extrem niedrigem Verkehr prüfen wir jetzt den Fall, bei dem
λin (und folglich λ′in) extrem groß ist. Man betrachte Router R2. Der bei Router R2
ankommende A-C-Verkehr (der bei R2 ankommt, nachdem er von R1 weitergeleitet
wurde) kann bei R2 eine Ankunftsrate haben, die meist R – die Kapazität der Verbindungsleitung von R1 zu R2 – entspricht, und zwar ungeachtet des Werts von λin.
Wenn λ′in für alle Verbindungen (einschließlich der B-D-Verbindung) extrem groß ist,
dann kann die Ankunftsrate des B-D-Verkehrs bei R2 viel größer als die des A-C-Verkehrs sein. Da der A-C- und der B-D-Verkehr bei Router R2 um begrenzten Pufferplatz konkurrieren müssen, wird der Umfang an A-C-Verkehr, der erfolgreich durch
R2 hindurchkommt (d. h. nicht aufgrund von Pufferüberlauf verloren geht), immer
kleiner, während die angebotene Last von B-D immer mehr wächst. Je mehr sich die
angebotene Last Unendlich nähert, füllt sich ein leerer Puffer bei R2 sofort mit einem
B-D-Paket und der Durchsatz der A-C-Verbindung bei R2 sinkt auf Null. Dies impliziert, dass der Ende-zu-Ende-Durchsatz von A nach C im Extremfall starken Verkehrsaufkommens auf Null sinkt. Diese Überlegungen führen zu dem in Abbildung 3.46 dargestellten Kompromiss zwischen der angebotenen Last und dem Durchsatz.
Der Grund für den eventuellen Abfall des Durchsatzes bei steigender angebotener
Last wird deutlich, wenn man sich den Umfang an verschwendeter »Arbeit« durch
das Netzwerk vor Augen hält. Wenn in dem oben beschriebenen Szenario mit hohem
Verkehrsaufkommen ein Paket vom Router des zweiten Hops verworfen wird, war
die »Arbeit« des Routers im ersten Hop, d. h. die Weiterleitung eines Pakets zum
Router des zweiten Hops, regelrecht umsonst. Das Netzwerk wäre genauso gut (oder
schlecht) dran, wenn der erste Router das Paket gleich verworfen hätte und untätig
Kapitel 3 – Transportschicht
240
Abbildung 3.46 Leistung in Szenario 3
λout
R/2
λ′in
geblieben wäre. Genauer gesagt, die beim ersten Router für die Weiterleitung des
Pakets zum zweiten Router verbrauchte Übertragungskapazität hätte sich für die
Übertragung eines anderen Pakets viel nutzbringender verwenden lassen. Beispielsweise könnte es sich bei der Auswahl eines Pakets zur Übertragung für einen Router
als besser erweisen, Paketen den Vorzug zu geben, die bereits einige Upstream-Router überquert haben.) Wir sehen hier also einen weiteren Kostenfaktor, der durch das Verwerfen eines Pakets aufgrund von Überlast entsteht: Wenn ein Paket entlang eines Pfads verworfen wird, wurde die dafür aufgewendete Übertragungskapazität in den Upstream-Routern
für die Weiterleitung dieses Pakets verschwendet.
3.6.2 Ansätze für Überlastkontrolle
Abschnitt 3.7 befasst sich ausführlicher mit dem spezifischen Ansatz für Überlastkontrolle in TCP. An dieser Stelle identifizieren wir die beiden allgemeinen Ansätze, auf
deren Grundlage in der Praxis Überlastkontrolle umgesetzt wird. Wir beschreiben
spezifische Netzwerkarchitekturen und Überlastkontrollprotokolle, die auf diesen
Ansätzen basieren.
Auf der allgemeinsten Ebene lassen sich die Ansätze für Überlastkontrolle danach
unterscheiden, ob die Vermittlungsschicht der Transportschicht für Überlastkontrollzwecke explizit Unterstützung bietet:
õ Ende-zu-Ende-Überlastkontrolle: Bei diesem Ansatz für Überlastkontrolle bietet die
Vermittlungsschicht der Transportschicht keine explizite Unterstützung für Überlastkontrollzwecke. Hier muss sogar das Vorhandensein einer Überlast im Netzwerk von den Endsystemen allein auf der Grundlage des beobachteten Netzwerkverhaltens (z. B. Paketverlust und Verzögerung) hergeleitet werden. Wir werden
in Abschnitt 3.7 sehen, dass TCP zwangsläufig diesen Ansatz für Überlastkontrolle anwenden muss, weil die IP-Schicht den Endsystemen hinsichtlich der Netzwerküberlast keinerlei Feedback liefert. Ein TCP-Segmentverlust (von dem nach
einem Timeout oder einer dreifachen Duplikatbestätigung ausgegangen wird)
3.6 Grundlagen der Überlastkontrolle
wird als Hinweis auf eine Netzwerküberlast betrachtet und TCP senkt seine Fenstergröße entsprechend. Wir werden auch noch sehen, dass neue Vorschläge für
TCP zunehmend höhere Werte der Roundtrip-Verzögerung als Hinweise steigender Netzwerküberlast heranziehen.
õ Vom Netzwerk unterstützte Überlastkontrolle: Bei diesem Ansatz bieten die Komponenten der Vermittlungsschicht (d. h. Router) dem Sender explizites Feedback
über den Überlastzustand im Netzwerk. Dieses Feedback kann sehr einfach sein,
z. B. ein einziges Bit, das auf eine Überlast in einer Verbindungsleitung hinweist.
Dieser Ansatz wurde in den alten Architekturen von IBM-SNA [Schwartz 1982]
und DECNET [Jain 1989; Ramakrishnan 1990] angewandt, kürzlich für TCP/IPNetzwerke vorgeschlagen [Floyd TCP 1994; RFC 2481] und auch in der ABRÜberlastkontrolle (Available Bit Rate) von ATM (siehe weiter unten) benutzt. Ein
umfangreicheres Netzwerk-Feedback ist auch möglich. Bei einer Form der ATMABR-Überlastkontrolle, die wir in einem der nächsten Abschnitte beschreiben,
kann beispielsweise ein Router den Sender explizit über die Übertragungsrate
informieren, die er auf einer abgehenden Verbindungsleitung unterstützen kann.
Bei der netzwerkunterstützten Überlastkontrolle werden Überlastinformationen normalerweise vom Netzwerk zum Sender auf eine von zwei Arten zurückgespeist
(siehe Abbildung 3.47). Direktes Feedback kann von einem Netzwerk-Router zum
Sender gesendet werden. Diese Form der Mitteilung ist normalerweise ein ChokePaket (was im Grunde besagt: »Ich bin überlastet!«). Die zweite Form der Mitteilung
erfolgt, wenn ein Router ein Feld in einem Paket, das vom Sender zum Empfänger
fließt, markiert bzw. aktualisiert, um auf eine Überlast hinzuweisen. Beim Empfang
eines so gekennzeichneten Pakets benachrichtigt der Empfänger dann den Sender
über den Überlasthinweis. Man beachte, dass diese Form der Mitteilung mindestens
eine volle Roundtrip-Zeit beansprucht.
Abbildung 3.47 Zwei Feedback-Pfade für Überlastinformationen vom Netzwerk
Host A
Netzwerk-Feedback
über Empfänger
Direktes NetzwerkFeedback
241
Kapitel 3 – Transportschicht
242
3.6.3 ABR-Überlastkontrolle in ATM
Die ausführliche Untersuchung der TCP-Überlastkontrolle in Abschnitt 3.7 enthält
auch eine Fallstudie über einen Ende-zu-Ende-Ansatz für Überlastkontrolle. Wir
beenden diesen Abschnitt mit einer kurzen Fallstudie der netzwerkunterstützten
Überlastkontrollmechanismen, die im ATM-ABR-Dienst benutzt werden. ABR wurde
als elastischer Datentransferdienst ausgelegt, der an TCP erinnert. Wenn das Netzwerk unterbelastet ist, sollte der ABR-Dienst in der Lage sein, die zusätzlich verfügbare Bandbreite zu nutzen. Ist das Netzwerk jedoch überlastet, sollte der ABR-Dienst
seine Übertragungsrate auf eine im Voraus festgelegte minimale Übertragungsrate
drosseln. Ein ausführlicher Lehrabschnitt über ATM-ABR-Überlastkontrolle und Verkehrsmanagement findet der Leser in [Jain 1996].
Abbildung 3.48 zeigt das Rahmenwerk der ATM-ABR-Überlastkontrolle. In unserer anschließenden Diskussion verwenden wir die ATM-Terminologie (z. B. den
Begriff »Switch« statt »Router« und »Zelle« statt »Paket«). Beim ATM-ABR-Dienst
werden Datenzellen von einer Quelle zu einem Ziel durch eine Reihe dazwischen liegender Switches übertragen. Mit den Datenzellen vermengt sind so genannte Ressourcenmanagement-Zellen (RM-Zellen). Wir werden bald sehen, dass diese RMZellen benutzt werden können, um überlastspezifische Informationen unter Hosts
und Switches zu verteilen. Wenn eine RM-Zelle am Ziel ist, wird sie »umgedreht«
und an den Sender zurückgeschickt (möglicherweise, nachdem das Ziel ihren Inhalt
modifiziert hat). Es ist auch möglich, dass ein Switch eine RM-Zelle selbst erzeugt
und sie direkt an eine Quelle sendet. RM-Zellen können also sowohl für das direkte
Netzwerk-Feedback als auch für das Netzwerk-Feedback über den Empfänger
benutzt werden (siehe Abbildung 3.48).
Abbildung 3.48 Überlastkontrolle im ATM-ABR-Dienst
Quelle
RM-Zellen
Datenzellen
Switch
Ziel
Switch
Die ATM-ABR-Überlastkontrolle ist ein ratenbasierter Ansatz. Das heißt, der Sender
berechnet explizit eine Höchstrate, in der er senden kann, und reguliert sich dann
selbst. ABR bietet drei Mechanismen für die Signalisierung von überlastspezifischen
Informationen von den Switches zum Empfänger:
õ EFCI-Bit: Jede Datenzelle enthält ein EFCI-Bit (Explicit Forward Congestion Indication). Ein überlasteter Netzwerk-Switch kann das EFCI-Bit in einer Datenzelle
auf 1 setzen, um dem Ziel-Host eine Überlast zu signalisieren. Das Ziel muss das
3.7 TCP-Überlastkontrolle
EFCI-Bit in allen empfangenen Datenzellen prüfen. Wenn eine RM-Zelle am Ziel
ankommt und in der zuletzt empfangenen Datenzelle das EFCI-Bit auf 1 gesetzt
war, dann setzt das Ziel das CI-Bit (Congestion Indication) der RM-Zelle auf 1 und
sendet die RM-Zelle an den Sender zurück. Durch Verwendung des EFCI-Bits in
Datenzellen und des CI-Bits in RM-Zellen kann ein Sender also über eine Überlast
in einem Netzwerk-Switch benachrichtigt werden.
õ CI- und NI-Bits: Wie oben erwähnt, sind RM-Zellen vom Sender zum Empfänger
mit Datenzellen vermengt. Die Rate der RM-Zellenvermengung ist ein einstellbarer Parameter; der Default-Wert ist eine RM-Zelle alle 32 Datenzellen. Diese RMZellen haben ein CI- (Congestion Indication) und ein NI-Bit (No Increase), die ein
überlasteter Netzwerk-Switch benutzen kann. Das heißt, ein Switch kann das NIBit in einer vorbeifließenden RM-Zelle auf 1 setzen, wenn die Überlast gering ist,
und unter starken Überlastbedingungen das CI-Bit auf 1 setzen. Wenn ein ZielHost eine RM-Zelle empfängt, sendet er sie mit den CI- und NI-Bits intakt an den
Sender zurück (außer, dass das Ziel als Ergebnis des oben beschriebenen EFCIMechanismus CI möglicherweise auf 1 gesetzt hat).
õ ER-Feld: Jede RM-Zelle enthält auch ein 2-Byte-ER-Feld (Explicit Rate). Ein überlasteter Switch kann den im ER-Feld enthaltenen Wert in einer vorbeifließenden
RM-Zelle senken. Auf diese Weise wird das ER-Feld auf eine minimale Rate
gesetzt, die von allen Switches auf dem Pfad von der Quelle zum Ziel unterstützt
wird.
Eine ATM-ABR-Quelle berichtigt die Rate, in der sie Zellen senden kann, als eine
Funktion der CI-, NI- und ER-Werte in einer zurückgegebenen RM-Zelle. Die Regeln
für diese Ratenberichtigung sind eher kompliziert und ein bisschen mühsam. Wir
empfehlen dem interessierten Leser [Jain 1996] das umfangreiche Details bereithält.
3.7 TCP-Überlastkontrolle
Dieser Abschnitt widmet sich wieder der Untersuchung von TCP. Wie wir aus
Abschnitt 3.5 wissen, bietet TCP einen zuverlässigen Transportdienst zwischen zwei
Prozessen, die auf unterschiedlichen Hosts laufen. Eine weitere sehr wichtige Komponente von TCP ist der Überlastkontrollmechanismus. Wie im vorherigen Abschnitt
erwähnt, muss TCP eine Ende-zu-Ende- und keine netzwerkunterstützte Überlastkontrolle anwenden, weil die IP-Schicht ihren Endsystemen kein explizites Feedback
hinsichtlich Netzwerküberlast bietet. Bevor wir auf die Details der TCP-Überlastkontrolle übergehen, geben wir einen Überblick über den Überlastkontrollmechanismus
von TCP sowie über das allgemeine Ziel, das TCP anstrebt, wenn mehrere TCP-Verbindungen sich die Bandbreite einer überlasteten Verbindungsleitung teilen müssen.
Eine TCP-Verbindung steuert ihre Übertragungsrate durch Einschränkung der
Anzahl von übertragenen, jedoch noch nicht bestätigten Segmenten. Wir nennen diese
Anzahl von zulässigen unbestätigten Segmenten w, die meist als TCP-Fenstergröße
bezeichnet wird. Im Idealfall sollte es TCP-Verbindungen gestattet sein, so schnell wie
möglich (d. h. die größtmögliche Anzahl anstehender unbestätigter Segmente) zu
übertragen, solange keine Segmente aufgrund von Überlast verloren gehen (bzw. von
den Routern verworfen werden). In einem sehr allgemeinen Sinn beginnt eine TCPVerbindung mit einem kleinen Wert von w und »tastet« dann die Verbindungsleitungen ihres Ende-zu-Ende-Pfads auf Vorhandensein unbenutzter Leitungsbandbreite
durch stufenweise Erhöhung von w ab. Die TCP-Verbindung fährt mit der Erhöhung
243
244
Kapitel 3 – Transportschicht
von w so lange fort, bis ein Segment verloren geht (was durch ein Timeout oder Duplikatbestätigungen erkannt wird). Im Fall eines Verlusts reduziert die TCP-Verbindung
w auf ein »sicheres Maß« und beginnt anschließend erneut mit dem Abtasten auf eine
mögliche unbenutzte Bandbreite, indem sie w langsam wieder erhöht.
Eine wichtige Messgröße der Leistung einer TCP-Verbindung ist ihr Durchsatz –
die Rate, in der sie Daten vom Sender zum Empfänger überträgt. Natürlich hängt der
Durchsatz von dem Wert von w ab. Wenn ein TCP-Sender alle w-Segmente nacheinander überträgt, muss er anschließend eine Roundtrip-Zeit (RTT) warten, bis er
Bestätigungen für diese Segmente erhält. An diesem Punkt kann er w weitere Segmente senden. Wenn eine Verbindung w Segmente mit einer Größe von MSS Byte alle
RTT Sekunden überträgt, dann beträgt der Durchsatz der Verbindung bzw. die Übertragungsrate (w · MSS)/RTT Bytes pro Sekunde.
Es sei gegeben, dass K TCP-Verbindungen eine Verbindungsleitung mit der Kapazität R benutzen, über diese Verbindungsleitung keine UDP-Pakete fließen, jede TCPVerbindung eine sehr große Datenmenge überträgt und keine dieser TCP-Verbindungen eine andere überlastete Verbindungsleitung durchquert. Im Idealfall sollten die
Fenstergrößen der TCP-Verbindungen, die diese Verbindungsleitung durchqueren,
ausreichend groß sein, so dass jede Verbindung einen Durchsatz von R/K erreicht.
Allgemeiner ausgedrückt: Wenn eine Verbindung durch N Verbindungsleitungen
läuft und die Verbindungsleitung n eine Übertragungsrate von Rn hat und insgesamt
Kn TCP-Verbindungen unterstützt, dann sollte diese Verbindung im Idealfall eine
Rate von Rn/Kn auf der n-ten Verbindungsleitung erreichen. Die durchschnittliche
Ende-zu-Ende-Rate dieser Verbindung kann aber die Mindestrate, die auf allen Verbindungsleitungen entlang des Ende-zu-Ende-Pfads erreicht wird, nicht übersteigen.
Das heißt, die Übertragungsrate von Ende-zu-Ende für diese Verbindung beträgt r =
min{R1/K1, …, RN/KN}. Wir können uns das Ziel von TCP so vorstellen, dass es dieser
Verbindung eine Ende-zu-Ende-Rate r bereitstellt. (In Wirklichkeit ist die Formel für r
komplizierter, weil man die Tatsache berücksichtigen muss, dass eine oder mehrere
der nutzenden Verbindungen auf irgendeiner anderen Verbindungsleitung, die nicht
auf diesem Ende-zu-Ende-Pfad liegt, auf einen Flaschenhals stößt und daher ihren
Bandbreitenanteil Rn/Kn nicht nutzen kann. In diesem Fall wäre der Wert von r höher
als min{R1/K1, …, RN/KN}; siehe [Bertsekas 1991].
3.7.1 Übersicht über die TCP-Überlastkontrolle
In Abschnitt 3.5 haben wir gesehen, dass sich jede Seite einer TCP-Verbindung aus
einem Empfangs- und Sendepuffer und mehreren Variablen (LastByteRead, RcvWin
usw.) zusammensetzt. Der Überlastkontrollmechanismus von TCP lässt jede Seite der
Verbindung zwei zusätzliche Variablen verwalten: Überlastfenster (Congestion Window) und Grenzwert (Threshold). Das Überlastfenster, CongWin, bringt die zusätzliche Einschränkung, wie viel Verkehr ein Host in eine Verbindung einspeisen kann.
Insbesondere darf die bei einem Host für eine TCP-Verbindung anstehende unbestätigte Datenmenge das Minimum von CongWin und RcvWin nicht übersteigen, d. h.:
LastByteSent – LastByteAcked ≤ min{CongWin, RcvWin}
Der Grenzwert, der weiter unten ausführlich beschrieben wird, ist eine Variable, die
sich darauf auswirkt, wie CongWin wächst.
Wir untersuchen jetzt, wie sich das Überlastfenster im Verlauf der Lebensdauer
einer TCP-Verbindung entwickelt. Um uns auf Überlastkontrolle (im Gegensatz zur
3.7 TCP-Überlastkontrolle
Flusskontrolle) zu konzentrieren, nehmen wir an, dass der TCP-Empfangspuffer so
groß ist, dass die Einschränkung des Empfangsfensters ignoriert werden kann. In diesem Fall ist die unbestätigte Datenmenge, die ein Host für eine TCP-Verbindung vorhalten darf, höchstens durch CongWin begrenzt. Außerdem nehmen wir an, dass ein
Sender eine sehr große Datenmenge an einen Empfänger senden muss.
Nachdem eine TCP-Verbindung zwischen den beiden Endsystemen aufgebaut
wurde, schreibt der Anwendungsprozess beim Sender in den TCP-Sendepuffer. TCP
entnimmt davon Stücke in der MSS-Größe, verkapselt jedes Stück in einem TCP-Segment und gibt die Segmente an die Vermittlungsschicht zur Übertragung im Netzwerk
weiter. Das TCP-Überlastfenster reguliert die Zeiten, in denen die Segmente in das Netzwerk gespeist (d. h. an die Vermittlungsschicht weitergegeben) werden. Anfangs entspricht das Überlastfenster einer MSS. TCP sendet das erste Segment zum Netzwerk
und wartet auf eine Bestätigung. Wird dieses Segment bestätigt, bevor sein Timer
abläuft, erhöht der Sender das Überlastfenster um eine MSS und sendet zwei Segmente
mit maximaler Größe. Wenn diese Segmente vor ihrem jeweiligen Timeout bestätigt
werden, erhöht der Sender das Überlastfenster um eine MSS pro bestätigtem Segment,
so dass das Überlastfenster jetzt vier MSS umfasst, und sendet vier Segmente mit maximaler Größe. Diese Prozedur wird fortgesetzt, (1) solange das Überlastfenster unter dem
Grenzwert liegt und (2) die Bestätigungen vor ihrem jeweiligen Timeout ankommen.
Während dieser Phase der Überlastkontrollprozedur erhöht sich das Überlastfenster exponentiell sehr schnell. Das Überlastfenster wird auf eine MSS initialisiert; nach
einer RTT wird das Fenster auf zwei Segmente erhöht; nach zwei Roundtrip-Zeiten
wird das Fenster auf vier Segmente erhöht; nach drei Roundtrip-Zeiten wird das
Fenster auf acht Segmente erhöht und so fort. Diese Phase des Algorithmus nennt
man Slow-Start (Langsamstart), weil sie mit einem kleinen, einer MSS entsprechenden Überlastfenster beginnt. (Die Übertragungsrate der Verbindung startet langsam,
beschleunigt aber schnell.)
Die Slow-Start-Phase endet, wenn die Fenstergröße den Wert von threshold
(Grenzwert) übersteigt. Ist das Überlastfenster größer als der aktuelle Wert von
threshold, wächst es linear und nicht mehr exponentiell. Wenn w der aktuelle Wert
des Überlastfensters und größer als threshold ist, ersetzt TCP w nach Ankunft von w
Bestätigungen durch w + 1. Als Auswirkung davon wird das Überlastfenster in jeder
RTT, in der die Menge von Bestätigungen für ein ganzes Fenster ankommt, um 1
erhöht. Diese Phase des Algorithmus wird als Überlastvermeidung (Congestion
Avoidance) bezeichnet.
Die Überlastvermeidungsphase wird fortgesetzt, solange die Bestätigungen vor
ihrem jeweiligen Timeout ankommen. Die Fenstergröße und damit die Rate, in der der
TCP-Sender senden kann, kann sich aber nicht ewig erhöhen. Irgendwann wird die
TCP-Rate so sein, dass eine der Verbindungsleitungen auf dem Pfad gesättigt wird
und ein Verlust (sowie ein daraus resultierendes Timeout beim Sender) eintritt. Wenn
ein Timeout erfolgt, wird der Wert von threshold auf die Hälfte des Werts des aktuellen Überlastfensters gesetzt und das Überlastfenster auf eine MSS zurückgesetzt. Der
Sender erhöht dann das Überlastfenster mit Hilfe der Slow-Start-Prozedur wieder
exponentiell sehr schnell, bis das Überlastfenster an den Grenzwert stößt. Fazit:
õ Solange das Überlastfenster unter dem Grenzwert liegt, wächst es exponentiell.
õ Steigt das Überlastfenster über den Grenzwert, wächst es linear.
õ Bei jedem Timeout wird der Grenzwert auf die Hälfte des aktuellen Überlastfensters zurückgesetzt und das Überlastfenster auf 1 gesetzt.
245
Kapitel 3 – Transportschicht
246
Wenn wir die Slow-Start-Phase ignorieren, sehen wir, dass TCP im Grunde seine
Fenstergröße pro RTT um 1 (und damit seine Übertragungsrate um einen zusätzlichen Faktor) erhöht, wenn sein Netzwerkpfad nicht überlastet ist, und um einen Faktor von 2 pro RTT senkt, wenn der Pfad überlastet ist. Aus diesem Grund wird TCP
oft als AIMD-Algorithmus (Additive-Increase, Multiplicative-Decrease) bezeichnet.
Die Evolution des TCP-Überlastfensters ist in Abbildung 3.49 dargestellt. In dieser
Abbildung ist der Grenzwert anfangs gleich 8 · MSS. Das Überlastfenster steigt exponentiell recht schnell während des Slow-Starts und erreicht dann bei der dritten Übertragung den Grenzwert. Anschließend steigt das Überlastfenster linear, bis nach
Übertragung 7 ein Verlust entsteht. Man beachte, dass das Überlastfenster 12 · MSS
ist, wenn der Verlust eintritt. Der Grenzwert wird dann auf 0,5 · CongWin = 6 · MSS
und das Überlastfenster auf 1 gesetzt; anschließend wird der Prozess fortgesetzt. Dieser Überlastkontrollalgorithmus stammt von V. Jacobson [Jacobson 1988]; eine Reihe
von Modifikationen zu Jacobsons erster Version des Algorithmus sind in Stevens
(1994) und in RFC 2581 beschrieben.
Wir fügen an dieser Stelle an, dass die Beschreibung des TCP-Slow-Starts hier idealisiert wurde. Ein anfängliches Fenster von bis zu zwei MSS ist ein vorgeschlagener
Standard [RFC 2581] und wird in einigen Implementierungen bereits angewandt.
Abbildung 3.49 Evolution des Überlastfensters von TCP
15
14
13
Überlastfenster (in Segmenten)
12
11
10
9
Grenzwert
8
7
Grenzwert
6
5
4
3
2
1
0
1
2
3
4
5
6
7
8
9
10
11 12
Anzahl der Übertragungen
13 14
15
3.7 TCP-Überlastkontrolle
Reise nach Nevada: Tahoe, Reno und Vegas
Der soeben beschriebene TCP-Überlastkontrollalgorithmus wird auch als Tahoe
bezeichnet. Ein Problem mit dem Tahoe-Algorithmus ist, dass die Senderseite der
Anwendung, wenn ein Segment verloren geht, eventuell längere Zeit auf ein Timeout
warten muss. Aus diesem Grund wird in den meisten Betriebssystemen eine Variante
von Tahoe namens Reno implementiert. Wie Tahoe setzt Reno sein Überlastfenster
beim Ablauf eines Timers auf ein Segment. Reno beinhaltet aber den Fast-RetransmitMechanismus, der in Abschnitt 3.5 beschrieben wurde. Wir erinnern uns, dass dieser
Mechanismus die Übertragung eines verworfenen Segments auslöst, wenn vor dem
Timeout des Segments drei Duplikat-ACKs für das Segment ankommen. Reno beinhaltet auch einen Fast-Recovery-Mechanismus, der im Grunde die Slow-Start-Phase
nach einem Fast-Retransmit annulliert. Dem interessierten Leser empfehlen wir [Stevens 1994] und [RFC 2581]. In [Cela 2000] sind interaktive Animationen von Überlastvermeidung, Slow-Start, Fast-Retransmit und Fast-Recovery in TCP enthalten.
Die meisten heutigen TCP-Implementierungen verwenden den Reno-Algorithmus. In der Literatur wird aber noch ein weiterer – der Vegas-Algorithmus –
beschrieben, der die Leistung von Reno verbessern kann. Während Tahoe und Reno
auf Überlast (d. h. auf Überlauf von Router-Puffern) reagieren, versucht Vegas, Überlast durch Wahrung eines guten Durchsatzes zu vermeiden. Das Grundkonzept von
Vegas ist (1) Überlast in den Routern zwischen Quelle und Ziel vor einem Paketverlust zu erkennen und (2) die Rate linear zu senken, wenn ein bevorstehender Paketverlust erkannt wird. Ob ein Paketverlust bevorsteht, wird durch Beobachtung der
Roundtrip-Zeiten festgestellt. Je länger die Roundtrip-Zeiten der Pakete, um so größer ist die Überlast in den Routern. Der Vegas-Algorithmus wird ausführlich in
[Brakmo 1995] behandelt; eine Untersuchung seiner Leistung findet sich in [Ahn
1995]. Zum Stand von 1999 wurde Vegas in den meisten gängigen TCP-Implementierungen nicht benutzt.
Wir betonen an dieser Stelle, dass sich die TCP-Überlastkontrolle im Laufe der
Jahre entwickelt hat und noch weiterentwickelt wird. Was für das Internet gut war,
als TCP-Verbindungen größtenteils SMTP-, FTP- und Telnet-Verkehr übertrugen, ist
nicht unbedingt gut für das heutige, vom Web dominierte Internet oder für das Internet der Zukunft, das wer weiß welche Dienste unterstützen wird.
Gewährleistet TCP Fairness?
In der obigen Diskussion wurde deutlich, dass mit dem Überlastkontrollmechanismus von TCP unter anderem das Ziel verfolgt wird, die Bandbreite einer Flaschenhalsleitung gleichmäßig zwischen den TCP-Verbindungen aufzuteilen, die über diese
Verbindungsleitung auf einen Flaschenhals stoßen. Warum sollte der AIMD-Algorithmus (Additive-Increase, Multiplicative-Decrease) von TCP dieses Ziel aber erreichen,
insbesondere vor dem Hintergrund, dass verschiedene TCP-Verbindungen zu unterschiedlichen Zeiten starten können und somit zu einem bestimmten Zeitpunkt unterschiedliche Fenstergrößen haben? [Chiu 1989] bietet eine elegante und intuitive
Erklärung, warum die TCP-Überlastkontrolle darauf hinausläuft, eine gleichmäßige
Nutzung der Bandbreite einer Flaschenhalsleitung unter konkurrierenden TCP-Verbindungen bereitzustellen.
Wir betrachten den einfachen Fall zweier TCP-Verbindungen, die eine einzige Verbindungsleitung mit Übertragungsrate R gemeinsam nutzen (siehe Abbildung 3.50).
Wir nehmen an, dass die beiden Verbindungen die gleiche MSS und RTT haben (so
dass sie dann den gleichen Durchsatz haben, falls ihre Überlastfenstergröße gleich
247
248
Kapitel 3 – Transportschicht
ist), dass sie eine große Datenmenge senden müssen und keine der übrigen TCP-Verbindungen oder UDP-Datagramme diese gemeinsame Verbindungsleitung durchqueren. Außerdem ignorieren wir die Slow-Start-Phase von TCP und nehmen an,
dass die TCP-Verbindungen die ganze Zeit im Überlastvermeidungsmodus (Additive-Increase, Multiplicative-Decrease) laufen.
Abbildung 3.50 Zwei TCP-Verbindungen, die eine einzige Flaschenhalsverbindung gemeinsam
nutzen
TCP-Verbindung 2
FlaschenhalsRouter,
Kapazität R
TCP-Verbindung 1
Abbildung 3.51 zeigt den von den beiden TCP-Verbindungen realisierten Durchsatz.
Soll TCP die Bandbreite der Verbindungsleitungen zwischen den beiden Verbindungen gleichmäßig nutzen, dann müsste der realisierte Durchsatz auf der 45-Grad-Linie
(»Nutzung der Bandbreite zu gleichen Anteilen«) liegen, die vom Ursprung ausgeht.
Im Idealfall sollte die Summe der beiden Durchsätze R entsprechen. (Sicherlich ist es
nicht wünschenswert, dass jede Verbindung zwar einen gleichen Anteil an der Leitungskapazität erhält, allerdings einen von Null!) Das Ziel sollte also sein, dass die
erreichten Durchsätze irgendwo nahe des Schnittpunkts der Linien »Nutzung der
Bandbreite zu gleichen Anteilen« und »Volle Bandbreitenauslastung« in Abbildung
3.51 liegen.
Angenommen, die TCP-Fenstergrößen sehen so aus, dass die Verbindungen 1 und
2 zu einem bestimmten Zeitpunkt die durch Punkt A in Abbildung 3.51 gekennzeichneten Durchsätze realisieren. Da die von den beiden Verbindungen gemeinsam verbrauchte Leitungsbandbreite kleiner als R ist, entsteht kein Verlust und beide Verbindungen werden ihr Fenster als Ergebnis des Überlastvermeidungsalgorithmus von
TCP um 1 pro RTT erhöhen. Der gemeinsame Durchsatz der beiden Verbindungen
verläuft also auf einer 45-Grad-Linie (bei gleicher Erhöhung für beide Verbindungen),
ausgehend von Punkt A. Irgendwann wird die von den beiden Verbindungen
gemeinsam verbrauchte Leitungsbandbreite größer als R sein und ein Paketverlust
eintreten. Angenommen, dass die Verbindungen 1 und 2 einen Paketverlust erleiden,
wenn sie die durch Punkt B gekennzeichneten Durchsätze realisieren. Die daraus
resultierenden Durchsätze liegen also auf Punkt C, etwa auf halber Strecke entlang
3.7 TCP-Überlastkontrolle
249
Abbildung 3.51 Durchsatz zweier TCP-Verbindungen
Nutzung der
Bandbreite zu
gleichen Anteilen
R
Volle
Bandbreitenauslastung
D
Durchsatz, Verbindung 2
B
C
A
Durchsatz, Verbindung 1
R
eines Vektors, der bei B beginnt und am Ursprung endet. Da die gemeinsame Bandbreitennutzung auf Punkt C unter R liegt, erhöhen die beiden Verbindungen wieder
ihre Durchsätze entlang der bei C beginnenden 45-Grad-Linie. Schließlich ereignet
sich noch ein Verlust, z. B. bei Punkt D, und die beiden Verbindungen senken ihre
Fenstergrößen wieder um einen Faktor von Zwei und so weiter. Sie sollten sich selbst
davon überzeugen, dass die von den beiden Verbindungen realisierte Bandbreite
letztlich entlang der Linie »Nutzung der Bandbreite zu gleichen Anteilen« schwankt.
Sie sollten sich auch selbst davon überzeugen, dass die beiden Verbindungen auf dieses Verhalten konvergieren, ungeachtet dessen, wo sie sich in dem zweidimensionalen Raum befinden! Obwohl dieses Szenario auf einer Reihe idealisierter Annahmen
gründet, bietet es dennoch einen guten Eindruck darüber, warum TCP zu einer Bandbreitennutzung zu gleichen Anteilen zwischen Verbindungen führt.
In unserem idealisierten Szenario gingen wir davon aus, dass nur TCP-Verbindungen die Flaschenhalsleitung durchqueren und nur eine einzige TCP-Verbindung
mit je einem Host/Ziel-Paar in Verbindung steht. In der Praxis sind diese beiden
Bedingungen normalerweise nicht gegeben, so dass Client/Server-Anwendungen
sehr ungleiche Anteile an der Bandbreite einer Verbindungsleitung erhalten können.
Viele Netzwerkanwendungen laufen über TCP statt über UDP, weil sie den zuverlässigen Transportdienst von TCP nutzen wollen. Ein Anwendungsentwickler, der TCP
wählt, erhält aber nicht nur den zuverlässigen Datentransfer, sondern auch die TCPÜberlastkontrolle. Wie oben ausgeführt, reguliert die TCP-Überlastkontrolle die Übertragungsrate einer Anwendung mittels Überlastfenstermechanismus. Viele Multimedia-Anwendungen laufen genau aus diesem Grund nicht über TCP. Sie wollen nicht,
Kapitel 3 – Transportschicht
250
dass man ihre Übertragungsrate drosselt, auch wenn das Netzwerk stark überlastet ist.
Insbesondere laufen die meisten Internet-Phone- und Internet-Videokonferenzanwendungen über UDP. Diese Anwendungen pumpen ihre Audio- und Videodaten bevorzugt in einer konstanten Rate in das Netzwerk. Sie nehmen gelegentliche Paketverluste
in Kauf, sind aber nicht bereit, ihre Raten zu Zeiten von Überlast auf einen »fairen«
Umfang zu reduzieren und dafür keine Pakete zu verlieren. Aus Sicht von TCP sind die
Multimedia-Anwendungen, die über UDP laufen, überhaupt nicht fair: Sie kooperieren
nicht mit den anderen Verbindungen und passen ihre Übertragungsraten nicht entsprechend an. Eine der großen Herausforderungen in den nächsten Jahren wird es sein,
Überlastkontrollmechanismen für das Internet zu entwickeln, die UDP-Verkehr daran
hindern, den Internet-Durchsatz völlig lahm zu legen [Floyd 1999].
Doch auch wenn manr UDP-Verkehr zu fairem Verhalten zwingen könnte, hätte
man das Problem mit der Fairness noch lange nicht gelöst. Der Grund dafür ist, dass
nichts eine über TCP laufende Anwendung daran hindern kann, mehrere parallele
Verbindungen zu verwenden. Beispielsweise nutzen Web-Browser oft mehrere parallele TCP-Verbindungen, um eine Web-Seite zu übertragen. (Die genaue Anzahl der
gleichzeitigen Verbindungen ist in den meisten Browsern konfigurierbar.) Nutzt eine
Anwendung mehrere parallele Verbindungen, erhält sie einen größeren Anteil an der
Bandbreite einer überlasteten Verbindungsleitung. Als Beispiel betrachte man eine
Verbindungsleitung mit der Rate R, die neun laufende Client/Server-Anwendungen
unterstützt, wobei jede eine TCP-Verbindung benutzt. Gesellt sich eine weitere
Anwendung dazu, die ebenfalls eine TCP-Verbindung benutzt, erhält jede Anwendung ungefähr die gleiche Übertragungsrate von R/10. Benutzt diese letzte Anwendung aber elf parallele TCP-Verbindungen, erhält sie eine unfaire Zuteilung von
mehr als R/2. Da Web-Verkehr im Internet vorherrscht, sind mehrere parallele Verbindungen absolut üblich.
Makroskopische Beschreibung der TCP-Dynamik
Man stelle sich die Übersendung einer sehr großen Datei über eine TCP-Verbindung
vor. Betrachtet man den von der Quelle gesendeten Verkehr aus makroskopischer
Sicht, so kann man die Slow-Start-Phase ignorieren. Die Verbindung ist tatsächlich
über eine relativ kurze Zeit in der Slow-Start-Phase, weil die Verbindung exponentiell
schnell aus der Phase herauswächst. Wenn wir die Slow-Start-Phase ignorieren,
wächst das Überlastfenster linear. Es wird bei Eintritt eines Verlusts halbiert, wächst
linear, wird bei einem erneuten Verlust wieder halbiert und so fort. Dies war der
Anlass für das Sägezahnverhalten von TCP [Stevens 1994], das in Abbildung 3.49
dargestellt ist.
Was ist der durchschnittliche Durchsatz einer TCP-Verbindung angesichts dieses
Sägezahnverhaltens? Während eines bestimmten Roundtrip-Intervalls ist die Rate, in
der TCP Daten sendet, eine Funktion des Überlastfensters und der aktuellen RTT.
Wenn die Fenstergröße w · MSS und die aktuelle Roundtrip-Zeit RTT ist, dann beträgt
die Übertragungsrate von TCP (w · MSS)/RTT. Während der Überlastvermeidungsphase tastet TCP auf zusätzliche Bandbreite ab, indem es w pro RTT um Eins erhöht,
bis ein Verlust eintritt. (W sei der Wert von w, wenn ein Verlust eintritt.) Wenn RTT
und W über die Dauer der Verbindung ungefähr konstant sind, liegt die TCP-Übertragungsrate im Bereich von
W ⋅ MSS
W ⋅ MSS
----------------------- bis ----------------------RTT
2RTT
3.7 TCP-Überlastkontrolle
251
Diese Annahmen führen zu einem höchst vereinfachten makroskopischen Modell für
das Verhalten von TCP im Dauerzustand. Das Netzwerk verwirft ein von der Verbindung ankommendes Paket, wenn die Fenstergröße der Verbindung auf W · MSS
steigt; das Überlastfenster wird dann halbiert und anschließend um eine MSS pro
Roundtrip-Zeit erhöht, bis es wieder W erreicht. Dieser Prozess wiederholt sich
immer wieder. Da der TCP-Durchsatz linear zwischen den beiden Extremwerten
steigt, ergibt sich Folgendes:
0, 75 ⋅ W ⋅ MSS
Durchschnittlicher Durchsatz einer Verbindung: --------------------------------------RTT
Mit diesem stark idealisierten Modell für die Dauerzustandsdynamik von TCP lässt
sich auch ein interessanter Ausdruck herleiten, der die Verlustrate einer Verbindung
mit ihrer verfügbaren Bandbreite in Bezug setzt [Mahdavi 1997]. Diese Herleitung ist
Thema der Übungen am Ende dieses Kapitels.
3.7.2 Latenz-Modellierung: statisches Überlastfenster
Viele TCP-Verbindungen transportieren relativ kleine Dateien von einem Host zu
einem anderen. Bei HTTP/1.0 wird beispielsweise jedes Objekt einer Web-Seite über
eine getrennte TCP-Verbindung übertragen und viele dieser Objekte sind kleine Textdateien oder winzige Icons. Bei der Übertragung einer kleinen Datei können der Verbindungsaufbau und der Slow-Start von TCP eine beträchtliche Auswirkung auf die
Latenz haben. In diesem Abschnitt präsentieren wir ein analytischen Modell, das die
Auswirkung des Verbindungsaufbaus und Slow-Starts auf die Latenz quantifiziert.
Für ein bestimmtes Objekt definieren wir Latenz als die Zeit von der Einleitung einer
TCP-Verbindung durch den Client bis zum Empfang des gesamten angeforderten
Objekts beim Client.
Die hier präsentierte Analyse gründet auf der Annahme, dass das Netzwerk nicht
überlastet ist, d. h. dass die TCP-Verbindung, die das Objekt befördert, die Bandbreite
der Verbindungsleitung nicht mit anderem TCP- oder UDP-Verkehr teilen muss. (Wir
kommentieren diese Annahme weiter unten.) Um die zentralen Fragen nicht zu verwässern, führen wir die Analyse außerdem in Zusammenhang mit dem einfachen
Netzwerk von Abbildung 3.52 durch, das nur über eine Verbindungsleitung verfügt.
(Diese Verbindungsleitung könnte einen einzigen Flaschenhals auf einem Ende-zuEnde-Pfad modellieren; siehe auch die Übungen bezüglich einer expliziten Erweiterung auf den Fall mit mehreren Verbindungsleitungen.)
Abbildung 3.52 Einfaches Netzwerk mit einer Verbindungsleitung, über die ein Client und ein
Server verbunden sind
R bps
Client
Server
252
Kapitel 3 – Transportschicht
Wir gehen außerdem von folgenden vereinfachenden Annahmen aus:
õ Die Datenmenge, die der Sender übertragen kann, ist ausschließlich durch das
Überlastfenster des Senders begrenzt. (Folglich sind die TCP-Empfangspuffer
groß.)
õ Es werden keine Pakete beschädigt, noch gehen welche verloren, so dass es keine
Neuübertragungen gibt.
õ Der gesamte Overhead aller Protokoll-Header – TCP-, IP- und SicherungsschichtHeader – ist verschwindend klein und kann ignoriert werden.
õ Das zu übertragende Objekt (d. h. die Datei) besteht aus einer Ganzzahl von Segmenten mit der Größe MSS (maximale Segmentgröße).
õ Die einzigen Pakete, die keine vernachlässigbaren Übertragungszeiten haben,
sind diejenigen, die TCP-Segmente mit maximaler Größe befördern. Segmente für
Anfragenachrichten, Bestätigungen und TCP-Verbindungsaufbau sind klein und
haben vernachlässigbare Übertragungszeiten.
õ Der anfängliche Grenzwert des TCP-Überlastkontrollmechanismus ist ein großer
Wert, der vom Überlastfenster nie erreicht wird.
Wir führen darüber hinaus folgende Notation ein:
õ Die Größe des zu übertragenden Objekts ist 0 Bit.
õ Die MSS (maximale Segmentgröße) ist S Bit (z. B. 536 Byte).
õ Die Übertragungsrate der Verbindungsleitung vom Server zum Client beträgt R
bps.
õ Die Roundtrip-Zeit wird als RTT bezeichnet.
In diesem Abschnitt definieren wir RTT als die Zeit, die verstreicht, bis ein kleines
Paket vom Client zum Server und dann wieder zum Client zurück reist, ausschließlich
der Übertragungszeit des Pakets. Enthalten sind die beiden Ausbreitungsverzögerungen
von Ende zu Ende zwischen den beiden Endsystemen und die Verarbeitungszeiten in
den beiden Endsystemen. Wir nehmen an, dass die RTT auch die Roundtrip-Zeit
eines Pakets, beginnend beim Server, ist.
Obwohl die in diesem Abschnitt dargestellte Analyse von einem nicht überlasteten Netzwerk mit einer einzigen TCP-Verbindung ausgeht, bietet sie einen guten Einblick in den realistischeren Fall eines überlasteten Netzwerks mit mehreren Verbindungsleitungen. Bei einem überlasteten Netzwerk stellt R grob die Bandbreitenmenge dar, die im Dauerzustand in der Netzwerkverbindung von Ende zu Ende verfügbar ist, während RTT für eine Roundtrip-Verzögerung steht, die Warteschlangenverzögerungen in den Routern vor den überlasteten Verbindungsleitungen beinhaltet. Im Falle des überlasteten Netzwerks modellieren wir jede TCP-Verbindung als
Verbindung mit einer konstanten Bitrate von R bps vor einer einzigen Slow-StartPhase. (Dies entspricht ungefähr dem Verhalten von TCP-Tahoe, wenn Verluste durch
dreifache Duplikatbestätigungen erkannt werden.) In unseren numerischen Beispielen verwenden wir Werte von R und RTT, die typische Werte eines überlasteten Netzwerk widerspiegeln.
Bevor wir mit der formellen Analyse beginnen, versuchen wir uns vorzustellen,
welche Latenz bestünde, wenn es keine Einschränkung durch das Überlastfenster
gäbe, d. h. wenn es dem Server gestattet wäre, Segmente nacheinander zu senden, bis
das gesamte Objekt gesendet wurde. Um diese Frage zu beantworten, beachte man
zuerst, dass eine RTT erforderlich ist, um die TCP-Verbindung einzuleiten. Nach
3.7 TCP-Überlastkontrolle
253
einer RTT sendet der Client eine Anfrage für das Objekt (das huckepack auf dem dritten Segment im Drei-Wege-Handshake von TCP gesendet wird). Nach insgesamt
zwei RTTs beginnt der Client, Daten vom Server zu empfangen. Der Client empfängt
Daten vom Server über eine Zeitdauer O/R, die Zeit, bis der Server das gesamte
Objekt übertragen hat. Folglich beläuft sich die Gesamtlatenz in dem Fall ohne Einschränkung durch das Überlastfenster auf 2 RTT + O/R. Dies stellt eine untere Grenze
dar; die Slow-Start-Prozedur mit ihrem dynamischen Überlastfenster wird diese
Latenz natürlich strecken.
Statisches Überlastfenster
Obwohl TCP ein dynamisches Überlastfenster nutzt, ist es lehrreich, zuerst den Fall
eines statischen Überlastfensters zu analysieren. Angenommen, W ist eine positive
Ganzzahl, die ein statisches Überlastfenster mit fester Größe bezeichnet. Bei einem
statischen Überlastfenster sind dem Server nicht mehr als W unbestätigte ausstehende Segmente gestattet. Wenn der Server die Anfrage vom Client erhält, sendet der
Server sofort W Segmente nacheinander an den Client. Der Server sendet dann für
jede Bestätigung, die er vom Client empfängt, ein Segment zum Netzwerk. Der SerAbbildung 3.53 Darstellung des Falls WS/R > RTT + S/R
TCP-Verbindung
einleiten
RTT
Objekt
anfordern
S/R
RTT
WS/R
erstes ACK
kommt zurück
O/R
Zeit
beim Client
Zeit
beim Server
254
Kapitel 3 – Transportschicht
ver fährt mit dem Senden eines Segments pro Bestätigung fort, bis alle Segmente des
Objekts gesendet wurden. Hier sind zwei Fälle zu berücksichtigen:
1. WS/R > RTT + S/R: In diesem Fall empfängt der Server eine Bestätigung für das
erste Segment im ersten Fenster, bevor er die Übertragung des ersten Fensters
abgeschlossen hat.
2. WS/R < RTT + S/R: In diesem Fall überträgt der Server die Segmentmenge des ersten Fensters, bevor er eine Bestätigung für das erste Segment im Fenster empfängt.
Wir betrachten zuerst Fall 1, der in Abbildung 3.53 dargestellt ist. In dieser Abbildung
ist die Fenstergröße W = 4 Segmente.
Eine RTT ist erforderlich, um die TCP-Verbindung einzuleiten. Nach einer RTT
sendet der Client eine Anfrage für das Objekt (das huckepack auf dem dritten Segment im Drei-Wege-Handshake von TCP gesendet wird). Nach insgesamt zwei RTTs
beginnt der Client, Daten vom Server zu empfangen. Vom Server kommen periodisch
alle S/R Sekunden Segmente an und der Client bestätigt jedes vom Server empfangene Segment. Da der Server die erste Bestätigung erhält, bevor er mit dem Senden
der Segmentmenge eines Fensters fertig ist, fährt der Server mit der Übertragung von
Segmenten fort, nachdem er die Segmentmenge des ersten Fensters übertragen hat.
Und weil die Bestätigungen beim Server periodisch alle S/R Sekunden ab der ersten
Bestätigung ankommen, überträgt der Server kontinuierlich Segmente, bis er das
gesamte Objekt übertragen hat. Nachdem der Server also mit der Übertragung des
Objekts in Rate R begonnen hat, fährt er mit der Übertragung des Objekts in Rate R
fort, bis das gesamte Objekt übertragen ist. Die Latenz beträgt somit 2 RTT + O/R.
Jetzt betrachten wir Fall 2, der in Abbildung 3.54 dargestellt ist. In dieser Abbildung ist die Fenstergröße W = 2 Segmente.
Wiederum beginnt der Client nach insgesamt zwei RTTs, Segmente vom Server zu
empfangen. Diese Segmente kommen periodisch alle S/R Sekunden an und der Client
bestätigt jedes vom Server empfangene Segment. Jetzt beendet der Server aber die
Übertragung des ersten Fensters, bevor die erste Bestätigung vom Client ankommt.
Nach dem Senden eines Fensters muss der Server deshalb aufhören und auf eine
Bestätigung warten, bevor er mit der Übertragung fortfährt. Wenn eine Bestätigung
ankommt, sendet der Server ein neues Segment an den Client. Nach der ersten Bestätigung kommen Bestätigungen für eine Fenstermenge an, wobei die einzelnen Bestätigungen in einem Abstand von S/R Sekunden einlaufen. Für jede dieser Bestätigungen sendet der Server genau ein Segment. Folglich wechselt der Server zwischen
zwei Zuständen: Im Übertragungszustand überträgt er W Segmente, während er im
Wartezustand nichts überträgt und auf eine Bestätigung wartet. Die Latenz entspricht
2 RTT zuzüglich der erforderlichen Zeit, bis der Server das Objekt überträgt, O/R,
zuzüglich der Zeit, die der Server im Wartezustand verharrt. Um die Zeit zu ermitteln, die der Server im Wartezustand verharrt, sei K = O/WS; wenn O/WS keine Ganzzahl ist, dann runden wir K auf die nächste Ganzzahl auf. Man beachte, dass K die
Anzahl der Fenster mit Daten ist, die im Objekt mit Größe O enthalten sind. Der Server ist zwischen der Übertragung jedes Fensters im Wartezustand, d. h. K – 1 Zeitperioden, wobei jede Periode RTT – (W – 1)S/R dauert (siehe Abbildung 3.54). Folglich
ergibt sich für Fall 2:
Latenz = 2 RTT + O/R + (K – 1) [S/R + RTT – WS/R]
3.7 TCP-Überlastkontrolle
255
Abbildung 3.54 Darstellung des Falls WS/R < RTT + S/R
TCP-Verbindung
einleiten
RTT
Objekt
anfordern
S/R
WS/R
RTT
erstes ACK
kommt zurück
Zeit
beim Client
Zeit
beim Server
Kombiniert man die beiden Fälle, erhält man:
Latenz = 2 RTT + O/R + (K – 1) [S/R + RTT – WS/R]+
wobei [x]+ = max(x,0).
Damit ist unsere Analyse statischer Fenster beendet. Die folgende Analyse für dynamische Fenster ist komplizierter, verläuft aber genauso wie die für statische Fenster.
3.7.3 Latenz-Modellierung: dynamisches Überlastfenster
Wir untersuchen jetzt die Latenz für einen Dateitransfer, wenn das dynamische Überlastfenster von TCP in Kraft ist. Der Server beginnt zuerst mit einem Überlastfenster
von einem Segment und sendet ein Segment an den Client. Wenn er eine Bestätigung
für das Segment erhält, erhöht er sein Überlastfenster auf zwei Segmente und sendet
(im Abstand von S/R Sekunden) zwei Segmente an den Client. Empfängt er die Bestätigungen für die beiden Segmente, erhöht er das Überlastfenster auf vier Segmente
und sendet (wiederum im Abstand von S/R Sekunden) vier Segmente an den Client.
Der Prozess wird dann fortgesetzt, wobei das Überlastfenster in jeder RTT verdoppelt
wird. Abbildung 3.55 zeigt ein Zeitablaufdiagramm für TCP.
Kapitel 3 – Transportschicht
256
Abbildung 3.55 Zeitlicher Ablauf von TCP während des Slow-Starts
TCP-Verbindung
einleiten
Objekt
anfordern
erstes Fenster
= S/R
zweites Fenster
= 2S/R
RTT
drittes Fenster
= 4S/R
viertes Fenster
= 8S/R
vollständige
Übertragung
Objekt
abgegeben
Zeit
beim Client
Zeit
beim Server
O/S ist die Anzahl der Segmente im Objekt; im obigen Diagramm ist O/S = 15. Man
betrachte die Anzahl von Segmenten, die sich in jedem der Fenster befinden. Das
erste Fenster enthält ein Segment, das zweite zwei und das dritte vier. Allgemeiner
ausgedrückt, enthält das k-te Fenster 2k–1 Segmente. Es sei gegeben, dass K die Anzahl
der Fenster ist, die das Objekt abdeckt; dann ist im obigen Diagramm K = 4. Im Allgemeinen können wir K in Bezug auf O/S wie folgt ausdrücken:

O
k–1
0
1
≥ ---- 
K = min  k : 2 + 2 + ... + 2
S


O
k
K = min k : 2 – 1 ≥ ---- 
S



O
K = min  k : k ≥ log 2  ---- + 1 
S



O
K = log 2  ---- + 1
S

3.7 TCP-Überlastkontrolle
Nach der Übertragung der Datenmenge eines Fensters kann der Server innehalten
(d. h. die Übertragung stoppen), während er auf eine Bestätigung wartet. In Abbildung 3.55 wartet der Server nach der Übertragung des ersten und zweiten Fensters,
jedoch nicht nach dem dritten. Wir berechnen jetzt die Wartezeit nach der Übertragung des k-ten Fensters. Der Zeitraum vom Beginn der Übertragung des k-ten Fensters durch den Server bis zu dem Zeitpunkt, wenn der Server eine Bestätigung für
das erste Segment im Fenster empfängt, beträgt S/R + RTT. Die Übertragungszeit des
k-ten Fensters ist (S/R) 2k–1. Die Wartezeit ist der Unterschied dieser beiden Größen,
d. h.
[ S ⁄ R + RTT – 2
k–1
(S ⁄ R)]
+
Der Server kann potenziell nach der Übertragung jedes der ersten k – 1 Fenster warten. (Der Server ist nach der Übertragung des k-ten Fensters fertig.) Wir können jetzt
die Latenz für die Übertragung der Datei berechnen. Die Latenz hat drei Komponenten: 2 RTT für die Einrichtung der TCP-Verbindung und Anforderung der Datei, O/R,
die Übertragungszeit des Objekts und die Summe aller Wartezeiten. Folglich gilt:
K–1
O
Latenz = 2RTT + ---- +
R
∑
+
S
k–1S
---- + RTT – 2 ---R
R
k=1
Der Leser sollte die obige Gleichung mit der Latenzgleichung für statische Überlastfenster vergleichen; alle Terme sind genau gleich, außer dass der Term WS/R für
dynamische Fenster durch 2k–1(S/R) ersetzt wurde. Um einen kompakteren Ausdruck
für die Latenz zu erhalten, sei Q die Zeit, in der der Server wartet, wenn das Objekt
eine unendliche Anzahl von Segmenten enthält:


S S k–1
Q = max k : RTT + ---- – ---- 2
≥0
R
R



RTT 
k–1
≤ 1 + -----------Q = max k : 2
S⁄R



RTT
Q = max k : k ≤ log 2  1 + ------------ + 1


S
⁄
R


Q =
RTT
log 2  1 + ------------ + 1

S ⁄ R
Die tatsächliche Zeit, die der Server wartet, ist P = min{Q, K – 1}. In Abbildung 3.55 ist
das P = Q = 2. Wenn wir die beiden obigen Gleichungen kombinieren, erhalten wir:
P
O
Latenz = ---- + 2RTT +
R
∑  RTT + ---R- – ---R- 2
S
k=1
S
k – 1

257
Kapitel 3 – Transportschicht
258
Wir können die obige Formel für Latenz weiter vereinfachen:
P
∑2
k–1
= 2P – 1
k=1
Kombiniert man die beiden obigen Gleichungen, erhält man folgenden Ausdruck für
Latenz in geschlossener Form:
S
S
O
P
Latenz = 2RTT + ---- + P RTT + ---- – ( 2 – 1 ) ---R
R
R
Um also die Latenz zu berechnen, müssen wir einfach K und Q berechnen, P = min
{Q, K – 1} setzen und P in die obige Formel einfügen.
Interessant ist ein Vergleich der TCP-Latenz mit der Latenz ohne Überlastkontrolle (d. h. ohne Einschränkung durch Überlastfenster). Ohne Überlastkontrolle ist
die Latenz 2 RTT + O/R, was wir als minimale Latenz bezeichnen. Wir können dann
Folgendes leicht aufzeigen:
Latenz
P
--------------------------------------------- ≤ 1 + ---------------------------------------------------Minimale Latenz
[ ( O ⁄ R ) ⁄ ( RTT ) ] + 2
Die obige Formel zeigt, dass der Slow-Start von TCP die Latenz nicht erheblich
erhöht, wenn RTT << O/R ist, d. h. wenn die Roundtrip-Zeit viel geringer als die
Übertragungszeit des Objekts ist. Wenn wir also ein relativ großes Objekt über eine
nicht überlastete Hochgeschwindigkeitsleitung senden, hat der Slow-Start eine unbedeutende Auswirkung auf die Latenz. Im Web übertragen wir aber oft viele kleine
Objekte auf überlasteten Verbindungsleitungen, so dass der Slow-Start die Latenz
beträchtlich erhöhen kann (wie Sie im nächsten Unterabschnitt sehen werden).
Im Folgenden werden einige Beispielszenarien betrachtet. In allen Szenarien gilt
S = 536 Byte; das ist ein üblicher Default-Wert für TCP. Wir verwenden eine RTT von
100 ms; dies ist ein typischer Wert für eine kontinentale oder interkontinentale Verzögerung über mäßig überlastete Verbindungsleitungen. Zuerst betrachten wir das Versenden eines eher großen Objekts mit der Größe O = 100 Kbyte. Die Anzahl der Fenster für dieses Objekt ist K = 8. Die folgende Tabelle listet die Auswirkung des SlowStart-Mechanismus auf die Latenz bei mehreren Übertragungsraten auf.
R
O/R
P
Minimale Latenz:
O/R + 2 RTT
Latenz mit SlowStart
28 Kbps
28,6 s
1
28,8 s
28,9 s
100 Kbps
8s
2
8,2 s
8,4 s
1 Mbps
800 ms
5
1s
1,5 s
10 Mbps
80 ms
7
0,28 s
0,98 s
Die obige Aufstellung zeigt, dass der Slow-Start bei einem großen Objekt nur eine
beträchtliche Verzögerung hinzufügt, wenn die Übertragungsrate hoch ist. Bei einer
niedrigeren Übertragungsrate kommen die Bestätigungen relativ schnell zurück und
3.7 TCP-Überlastkontrolle
259
TCP erreicht rasch wieder seine maximale Rate. Wenn beispielsweise R = 100 Kbps
ist, beträgt die gesamte Wartezeit P = 2, während die Anzahl der zu übertragenden
Fenster K = 8 ist. Folglich wartet der Server nur jeweils nach den ersten beiden von
insgesamt acht Fenstern. Ist andererseits R = 10 Mbps, wartet der Server nach jedem
Fenster, was zu einer beträchtlichen Erhöhung der Verzögerung führt.
Als Nächstes betrachten wir das Versenden eines kleinen Objekts mit der Größe
O = 5 Kbyte. Die Anzahl der Fenster für dieses Objekt ist K = 4. Die folgende Tabelle
listet die Auswirkung des Slow-Start-Mechanismus auf die Latenz bei verschiedenen
Übertragungsraten auf.
R
O/R
P
Minimale Latenz:
O/R + 2 RTT
Latenz mit SlowStart
28 Kbps
1,43 s
1
1,63 s
1,73 s
100 Kbps
0,4 s
2
0,6 s
0,757 s
1 Mbps
40 ms
3
0,24 s
0,52 s
10 Mbps
4 ms
3
0,20 s
0,50 s
Wiederum fügt der Slow-Start eine beträchtliche Verzögerung hinzu, wenn die Übertragungsrate hoch ist. Wenn beispielsweise R = 1 Mbps ist, wartet der Server nach
jedem Fenster, so dass die Latenz mehr als das Doppelte der minimalen Latenz
beträgt.
Bei einer größeren RTT wird die Auswirkung des Slow-Starts für kleine Objekte
bei kleineren Übertragungsraten bedeutsam. Die folgende Tabelle enthält eine Aufstellung der Auswirkungen des Slow-Starts bei RTT = 1 Sekunde und O = 5 Kbyte
(K = 4).
R
O/R
P
Minimale Latenz:
O/R + 2 RTT
Latenz mit SlowStart
28 Kbps
1,43 s
3
3,4 s
5,8 s
100 Kbps
0,4 s
3
2,4 s
5,2 s
1 Mbps
40 ms
3
2,0 s
5,0 s
10 Mbps
4 ms
3
2,0 s
5,0 s
Zusammenfassend kann man sagen, dass der Slow-Start die Latenz erheblich erhöhen kann, wenn die Objektgröße relativ klein und die RTT relativ groß ist. Leider ist
dies ein häufiges Szenario, wenn Objekte über das World Wide Web gesendet werden.
Ein Beispiel: HTTP
Als eine Anwendung für unsere Analyse der Latenz berechnen wir die Reaktionszeit
für eine Web-Seite, die über das nicht persistente HTTP gesendet wird. Angenom-
260
Kapitel 3 – Transportschicht
men, die Seite besteht aus einer HTML-Basisseite und M referenzierten Bildern. Der
Einfachheit halber nehmen wir an, dass jedes der M + 1 Objekte genau O Bit enthält.
Beim nicht persistenten HTTP wird jedes Objekt unabhängig – eines nach dem
anderen – übertragen. Die Reaktionszeit der Web-Seite ist daher die Summe der
Latenzen für die einzelnen Objekte:

S
O
S
P
Reaktionszeit = ( M + 1 )  2RTT + ---- + P RTT + ---- – ( 2 – 1 ) ---- 
R
R
R


Die Reaktionszeit für nicht persistentes HTTP nimmt folgende Form an:
Reaktionszeit = (M + 1)O/R + 2M + 1)RTT + Latenz durch TCP-Slow-Start für
jedes der M + 1 Objekte
Wenn die Web-Seite viele Objekte enthält und die RTT groß ist, hat nicht persistentes
HTTP natürlich eine schlechte Reaktionszeit bzw. Leistung. In den Übungen wird die
Reaktionszeit für andere HTTP-Transportschemata, darunter persistente und nicht
persistente Verbindungen mit parallelen Verbindungen, behandelt. Dem Leser wird
die Durchsicht einer damit zusammenhängenden Analyse in [Heidemann 1997] empfohlen.
3.8 Zusammenfassung
Wir haben dieses Kapitel mit einer Untersuchung der Dienste begonnen, die ein Protokoll der Transportschicht den Netzwerkanwendungen bereitstellen kann. In einem
Extrem ist das Transportprotokoll sehr einfach und bietet den Anwendungen äußerst
einfache Dienste, d. h. nur Multiplexen/Demultiplexen für Kommunikationsprozesse. Das UDP-Protokoll des Internets ist ein Beispiel für ein solches einfaches Transportprotokoll. Im anderen Extrem kann ein Transportprotokoll eine Vielzahl von
Zusicherungen für Anwendungen bieten, z. B. zuverlässige Übertragung von Daten
sowie zugesicherte Verzögerungen und Bandbreiten. Dennoch sind die Dienste, die
ein Transportprotokoll bereitstellen kann, oft durch das Dienstmodell des Protokolls
auf der zugrunde liegenden Vermittlungsschicht begrenzt. Wenn das Protokoll der
Vermittlungsschicht für die Segmente der Transportschicht keine Verzögerungen
oder Bandbreiten zusichern kann, dann kann das Protokoll der Transportschicht auch
keine Verzögerungen oder Bandbreiten für die zwischen Prozessen ausgetauschten
Nachrichten zusichern.
Abschnitt 3.4 hat gezeigt, dass ein Transportprotokoll zuverlässigen Datentransfer
auch dann bereitstellen kann, wenn die zugrunde liegende Vermittlungsschicht
unzuverlässig ist. Die Bereitstellung eines zuverlässigen Datentransfers umfasst viele
komplexe Aspekte, lässt sich aber meistern, wenn man Bestätigungen, Timer, Neuübertragungen und Sequenznummern sorgfältig kombiniert.
Obwohl wir in diesem Kapitel zuverlässigen Datentransfer behandelt haben,
sollte man bedenken, dass zuverlässiger Datentransfer grundsätzlich von Protokollen
auf der Sicherungs-, Vermittlungs-, Transport- und Anwendungsschicht bereitgestellt
werden kann. Jede der oberen vier Schichten des Protokollstacks kann Bestätigungen,
Timer, Neuübertragungen und Sequenznummern implementieren und der jeweils
darunter liegenden Schicht zuverlässigen Datentransfer bieten. Im Laufe der Jahre
haben Techniker und Computerwissenschaftler unabhängig Protokolle für die Siche-
3.8 Zusammenfassung
rungs-, Vermittlungs-, Transport- und Anwendungsschicht entwickelt, die zuverlässigen Datentransfer bereitstellen (allerdings sind viele dieser Protokolle still und leise
wieder verschwunden).
In Abschnitt 3.5 wurde TCP, das verbindungsorientierte und zuverlässige Transportprotokoll des Internets, genauer untersucht. Sie haben erfahren, dass TCP komplex ist und Verbindungsmanagement, Flusskontrolle, Schätzungen der RoundtripZeit sowie zuverlässigen Datentransfer umfasst. Tatsächlich ist TCP noch komplexer
als in unserer Beschreibung: Wir haben absichtlich eine Vielzahl von TCP-Patches,
-Fixes und Verbesserungen nicht erwähnt, die in vielen TCP-Versionen implementiert
werden. Diese Komplexität wird allerdings vor der Netzwerkanwendung verborgen.
Wenn ein Client auf einem Host Daten zuverlässig an einen Server auf einem anderen
Host senden will, öffnet er einfach ein TCP-Socket zum Server und pumpt die Daten
in dieses Socket. Die Client/Server-Anwendung hat von der ganzen TCP-Komplexität keine Kenntnis.
In Abschnitt 3.6 wurde Überlastkontrolle aus einer allgemeinen Perspektive
behandelt, während in Abschnitt 3.7 beschrieben wurde, wie TCP Überlastkontrolle
implementiert. Sie haben erfahren, dass Überlastkontrolle für ein gut funktionierendes Netzwerk unerlässlich ist. Ohne Überlastkontrolle kann ein Netzwerk schnell in
einen Stau geraten, so dass kaum oder überhaupt keine Daten mehr von Ende zu
Ende übertragen werden können. Abschnitt 3.7 hat gezeigt, dass TCP einen Überlastkontrollmechanismus von Ende zu Ende implementiert, der seine Übertragungsrate
»additiv« erhöht, wenn der Pfad der TCP-Verbindung als nicht überlastet gilt, und sie
»multiplikativ« senkt, wenn ein Datenverlust erkannt wird. Mit diesem als »Additive
Increase / Multiplicative Decrease« bezeichneten Mechanismus wird auch angestrebt, jeder TCP-Verbindung auf einer überlasteten Verbindungsleitung einen fairen
Anteil an der Leitungsbandbreite zukommen zu lassen. Außerdem wurde die Auswirkung des TCP-Verbindungsaufbaus und des Slow-Starts auf die Latenz geprüft.
Wir haben festgestellt, dass Verbindungsaufbau und Slow-Start in vielen wichtigen
Szenarien erheblich zur Ende-zu-Ende-Verzögerung beitragen. Außerdem wurde
deutlich gemacht, dass die TCP-Überlastkontrolle immer noch einen Bereich intensiver Forschungsarbeiten darstellt und im Laufe der nächsten Jahre sicherlich weiterentwickelt wird.
In Kapitel 1 wurde gesagt, dass sich ein Computernetzwerk in die »Netzwerkperipherie« und den »Netzwerkkern« aufteilen lässt. Die Netzwerkperipherie deckt alles
ab, was in den Endsystemen passiert. Nachdem wir die Anwendungs- und Transportschicht behandelt haben, ist unsere Diskussion der Netzwerkperipherie abgeschlossen. Nun ist es an der Zeit, den Netzwerkkern zu erforschen! Diese Reise
beginnt im nächsten Kapitel mit der Vermittlungsschicht und wird in Kapitel 5 mit
der Sicherungsschicht fortgeführt.
261
Kapitel 3 – Transportschicht
262
WIEDERHOLUNGSFRAGEN
Abschnitte 3.1 bis 3.3
1. Man betrachte eine TCP-Verbindung zwischen Host A und Host B. Angenommen, die
TCP-Segmente von Host A nach Host B haben Quellportnummer x und Zielportnummer y.
Wie lauten die Quell- und Zielportnummern für die Segmente von Host B nach Host A?
2. Beschreiben Sie, warum sich ein Anwendungsentwickler möglicherweise dafür entscheidet, eine Anwendung über UDP und nicht über TCP zu betreiben.
3. Kann eine Anwendung in den Genuss eines zuverlässigen Datentransfers kommen,
auch wenn sie über UDP läuft? Falls ja, wie?
Abschnitt 3.5
4. Richtig oder falsch:
a. Host A sendet Host B eine große Datei über eine TCP-Verbindung. Host B hat
keine Daten an A zu senden. Host B sendet keine Bestätigungen an Host A, weil
Host B die Bestätigungen nicht Huckepack auf den Daten senden kann.
b. Die Größe des TCP-Empfangsfensters RcvWindow ändert sich nie während der
gesamten Dauer der Verbindung.
c. Host A sendet Host B eine große Datei über eine TCP-Verbindung. Die Anzahl der unbestätigten Bytes, die A sendet, darf die Größe des Empfangspuffers nicht übersteigen.
d. Host A sendet eine große Datei an Host B über eine TCP-Verbindung. Wenn die
Sequenznummer für ein Segment dieser Verbindung m ist, dann lautet die
Sequenznummer für das anschließende Segment m + 1.
e. Das TCP-Segment hat in seinem Header ein Feld für RcvWindow.
f. Die letzte SampleRTT in einer TCP-Verbindung ist beispielsweise gleich 1 s. Dann
wird Timeout für die Verbindung notwendigerweise auf einen Wert von > = 1 s
gesetzt.
g. Host A sendet Host B ein Segment mit Sequenznummer 38 und 4 Datenbyte. Die
Bestätigungsnummer im gleichen Segment muss dann 42 sein.
5. Angenommen, A sendet zwei aufeinander folgende TCP-Segmente an B. Das erste
Segment hat Sequenznummer 90 und das zweite 110.
a. Wie viele Daten enthält das erste Segment?
b. Wenn beispielsweise das erste Segment verloren geht, das zweite aber bei B ankommt,
wie lautet dann die Bestätigungsnummer in der Bestätigung, die B an A sendet?
6. Man betrachte das Telnet-Beispiel in Abschnitt 3.5. Ein paar Sekunden, nachdem der
Benutzer den Buchstaben ‚C’ eingegeben hat, tippt er den Buchstaben ‚R’. Wie viele
Segmente werden nach dem Eintippen des Buchstabens ‚R’ gesendet und was wird in
die Sequenznummern- und Bestätigungsfelder der Segmente eingefügt?
Abschnitt 3.7
7. Angenommen, zwei TCP-Verbindungen sind auf einer Flaschenhalsleitung mit Rate R
bps aktiv. Beide Verbindungen müssen eine riesige Datei (in der gleichen Richtung
über die Flaschenhalsleitung) senden. Die Übertragungen der Dateien beginnen zur
gleichen Zeit. Welche Übertragungsrate kann TCP den beiden Verbindungen jeweils
zur Verfügung stellen?
8. Richtig oder falsch: Wenn bei der Überlastkontrolle in TCP ein Timer beim Sender
abläuft, wird der Grenzwert auf die Hälfte seines vorherigen Werts gesetzt.
Übungen
263
ÜBUNGEN
3.1
Angenommen, Client A leitet eine Telnet-Sitzung zu Server S ein. Etwa zur gleichen
Zeit leitet auch Client B eine Telnet-Sitzung zu Server S ein. Geben Sie die möglichen Quell- und Zielportnummern für:
a. die von A nach S gesendeten Segmente.
b. die von B nach S gesendeten Segmente.
c. die von S nach A gesendeten Segmente.
d. die von S nach B gesendeten Segmente.
e. Wenn A und B unterschiedliche Hosts sind, ist es dann möglich, dass die Quellportnummer in den Segmenten von A nach S die gleiche wie die von B nach S
ist?
f. Was ist, wenn es sich bei A und B um den gleichen Host handelt?
3.2
UDP und TCP verwenden das Einer-Komplement für ihre Prüfsummen. Angenommen, Sie haben die folgenden drei 8-Bit-Wörter: 01010101, 01110000,
11001100. Wie lautet das Einer-Komplement für die Summe dieser Wörter? Zeigen Sie die ganze Berechnung. Warum nimmt UDP das Einer-Komplement der
Summe; warum wird nicht einfach die Summe verwendet? Wie erkennt der Empfänger Fehler, wenn das Einer-Komplement-Schema angewandt wird? Ist es möglich, dass ein 1-Bit-Fehler unerkannt bleibt? Was ist mit einem 2-Bit-Fehler?
3.3
Betrachten Sie unsere Motivation für die Korrektur von Protokoll rdt2.1. Zeigen
Sie, dass dieser Empfänger, wenn er mit dem Sender von Abbildung 3.11 läuft,
dazu führen kann, dass Sender und Empfänger in eine Verklemmung geraten können, was bedeutet, dass beide auf ein Ereignis warten, das nie eintritt.
rdt_rcv(rcvpkt)
&& notcorrupt(rcvpkt)
&& has_seq0(rcvpkt)
extract(rcvpkt,data)
deliver_data(data)
compute chksum
make_pkt(sendpkt,ACK,chksum)
udt_send(sndpkt)
rdt_rcv(rcvpkt)
&& (corrupt(rcvpkt) ||
has_seq0(rcvpkt))
rdt_rcv(rcvpkt)
&& (corrupt(rcvpkt) ||
has_seq1(rcvpkt))
compute chksum
make_pkt(sndpkt,NAK,chksum)
udt_send(sndpkt)
warte auf
0 von
unten
warte auf
1 von
unten
compute chksum
make_pkt(sndpkt,NAK,chksum)
udt_send(sndpkt)
rdt_rcv(rcvpkt)
&& notcorrupt(rcvpkt)
&& has seq1(recvpkt)
extract(rcvpkt,data)
deliver_data(data)
compute chksum
make_pkt(sendpkt,ACK,chksum)
udt_send(sndpkt)
3.4
In Protokoll rdt3.0 haben die ACK-Pakete, die vom Empfänger zum Sender fließen, keine Sequenznummern (obwohl sie ein ACK-Feld haben, das die Sequenznummer des Pakets enthält, das sie bestätigen). Warum brauchen unsere ACKPakete keine Sequenznummern?
3.5
Zeichnen Sie die FSM für die Empfängerseite von Protokoll rdt3.0.
➔
264
➔
Kapitel 3 – Transportschicht
3.6 Beschreiben Sie kurz den Betrieb von Protokoll rdt3.0, wenn Daten- und Bestätigungspakete beschädigt werden. Ihre Kurzbeschreibung sollte derjenigen von
Abbildung 3.16 ähneln.
3.7 Betrachten Sie einen Kanal, der Pakete verlieren kann, aber eine maximale
bekannte Verzögerung hat. Modifizieren Sie das Protokoll rdt2.1, um ein SenderTimeout und Retransmit einzubeziehen. Erklären Sie, warum Ihr Protokoll korrekt
über diesen Kanal kommunizieren kann.
3.8 Die Senderseite von rdt3.0 ignoriert einfach alle empfangenen Pakete (d. h., sie
unternimmt keine Aktion), die fehlerhaft sind oder – wenn es sich um ein Bestätigungsfeld handelt – im Bestätigungsnummernfeld einen falschen Wert haben. Wir
nehmen beispielsweise an, dass rdt3.0 unter solchen Umständen das aktuelle
Datenpaket einfach erneut überträgt. Würde das Protokoll immer noch funktionieren? (Hinweis: Überlegen Sie, was passiert, wenn nur Bitfehler auftreten würden;
es gäbe keine Paketverluste, aber vorzeitige Timeouts. Überlegen Sie auch, wie oft
das n-te Paket gesendet wird, wenn sich n Unendlich nähert.)
3.9 Betrachten Sie das Beispiel in Abbildung 3.17. Wie groß müsste die Fenstergröße
sein, um eine Kanalauslastung von mehr als 90% zu erreichen?
3.10 Entwerfen Sie ein zuverlässiges Datentransferprotokoll mit Pipelining, das nur
negative Bestätigungen verwendet. Wie schnell würde Ihr Protokoll auf verlorene
Pakete reagieren, wenn die Ankunftsrate von Daten zum Sender niedrig ist? Und
wenn sie hoch ist?
3.11 Bei dem generischen Selective-Repeat-Protokoll (SR), das in Abschnitt 3.4.4
beschrieben wurde, überträgt der Sender eine Nachricht, sobald sie verfügbar ist
(falls sie sich im Fenster befindet), ohne auf eine Bestätigung zu warten. Angenommen, wir wünschen uns ein SR-Protokoll, das jeweils zwei Nachrichten gleichzeitig
sendet. Das heißt, der Sender sendet zuerst zwei Nachrichten als Paar und das
nächste Paar erst, wenn er weiß, dass die ersten beiden korrekt empfangen wurden. Wir nehmen an, dass der Kanal zwar Nachrichten verlieren kann, dass aber
keine beschädigt oder umgeordnet werden. Entwerfen Sie ein Fehlerkontrollprotokoll für den unidirektionalen zuverlässigen Transfer von Nachrichten. Erstellen Sie
eine FSM-Beschreibung des Senders und des Empfängers. Beschreiben Sie das
Format der zwischen Sender und Empfänger ausgetauschten Pakete. Wenn Sie
einen anderen als die in Abschnitt 3.4 beschriebenen Prozeduraufrufe (z. B.
udt_send(), start_timer(), rdt_rcv() usw.) verwenden, beschreiben Sie klar die
jeweiligen Aktionen. Geben Sie ein Beispiel (eine Timeline-Übersicht für Sender
und Empfänger), um aufzuzeigen, wie Ihr Protokoll nach einem verlorenen Paket
fortfährt (Recovery).
3.12 Betrachten Sie ein Szenario, bei dem ein Host A gleichzeitig Nachrichten an die
Hosts B und C senden will. A ist mit B und C über einen Broadcast-Kanal verbunden. Ein von A gesendetes Paket wird über den Kanal also an B und C befördert.
Der Broadcast-Kanal, über den A, B und C verbunden sind, kann unabhängig
Nachrichten verlieren und beschädigen (so dass z. B. eine Nachricht von A bei B
korrekt ankommt, nicht aber bei C). Entwerfen Sie ein Stop-and-Wait-ähnliches
Fehlerkontrollprotokoll für die zuverlässige Übertragung eines Pakets von A nach B
und C, so dass A die Daten von der höheren Schicht erst erhält, wenn er weiß,
dass sowohl B als auch C das aktuelle Paket korrekt empfangen haben. Erstellen
Sie FSM-Beschreibungen von A und C. (Hinweis: Die FSM für B und C sollte im
➔
Übungen
➔
265
Wesentlichen gleich sein.) Außerdem beschreiben Sie das bzw. die verwendeten
Paketformate.
3.13
Betrachten Sie das Go-Back-N-Protokoll mit einer Senderfenstergröße von 3 und
einem Sequenznummerbereich von 1.024. Angenommen, dass zum Zeitpunkt t
das nächste Paket der Reihenfolge, das der Empfänger erwartet, eine Sequenznummer von k hat. Gehen Sie davon aus, dass das Medium die Reihenfolge der
Nachrichten nicht verändert. Beantworten Sie folgende Fragen:
a. Welche Serien von Sequenznummern können sich zum Zeitpunkt t im Senderfenster befinden? Erklären Sie Ihre Antwort.
b. Welche Werte können sich im ACK-Feld der Nachricht befinden, die sich in
Zeitpunkt t zum Sender zurück ausbreitet? Erklären Sie Ihre Antwort.
3.14
Gehen Sie von zwei Netzwerkeinheiten A und B aus. B hat Datennachrichten
anstehen, die entsprechend den folgenden Konventionen an A gesendet werden.
Wenn A eine Anfrage von der höheren Schicht erhält, die nächste Datennachricht
(D) von B zu holen, muss A eine Anfragenachricht (R) auf dem Kanal von A nach
B an B senden. Nur falls B eine R-Nachricht empfängt, kann sie eine Datennachricht (D) auf dem Kanal von B nach A an A zurücksenden. A sollte genau eine
Kopie von jeder D-Nachricht an die höhere Schicht abgeben. R-Nachrichten können im Kanal von A nach B verloren gehen (aber nicht beschädigt werden). Einmal versendete D-Nachrichten werden immer korrekt zugestellt. Die Verzögerung
auf beiden Kanälen ist unbekannt und variabel. Entwerfen Sie eine FSMBeschreibung eines Protokolls, das die entsprechenden Mechanismen implementiert, um den verlustbehafteten Kanal von A nach B zu kompensieren, und das
Nachrichten implementiert, die an die höhere Schicht in Einheit A weitergegeben
werden. Verwenden Sie nur die Mechanismen, die absolut notwendig sind.
3.15
Betrachten Sie die Go-Back-N- und Selective-Repeat-Protokolle. Angenommen,
der Sequenznummernraum hat Größe k. Welche maximal zulässige Größe kann
das Senderfenster haben, so dass Probleme wie die in Abbildung 3.26 bei beiden
Protokollen nicht vorkommen können?
3.16
Beantworten Sie folgende Fragen mit »Richtig« oder »Falsch« und erklären Sie
kurz Ihre Antwort:
a. Beim Selective-Repeat-Protokoll ist es möglich, dass der Sender ein ACK für
ein Paket empfängt, das außerhalb seines aktuellen Fensters liegt.
b. Bei Go-Back-N ist es möglich, dass der Sender ein ACK für ein Paket empfängt, das außerhalb seines aktuellen Fensters liegt.
c. Das Alternating-Bit-Protokoll ist das Gleiche wie das Selective-Repeat-Protokoll, mit einer Sender- und Empfängerfenstergröße von 1.
d. Das Alternating-Bit-Protokoll ist das Gleiche wie das Go-Back-N-Protokoll, mit
einer Sender- und Empfängerfenstergröße von 1.
3.17
Denken Sie an die Übertragung einer sehr großen Datei mit L Byte von Host A an
Host B. Gehen Sie von einer MSS von 1.460 Byte aus.
a. Welcher maximale Wert von L ist möglich, so dass TCP-Sequenznummern
nicht erschöpft werden? Denken Sie daran, dass das Sequenznummernfeld in
TCP vier Byte groß ist.
b. Für den in a. ermittelten L-Wert stellen Sie fest, wie lange es dauert, um die
Datei zu übertragen. Gehen Sie davon aus, dass an jedes Segment ein Header der Transport-, Ver-mittlungs-, und Sicherungsschicht von insgesamt 66
➔
Kapitel 3 – Transportschicht
266
➔
Byte angefügt wird, bevor das resultierende Paket über eine 10-Mbps-Verbindungsleitung versendet wird. Ignorieren Sie Fluss- und Überlastkontrolle, was
bedeutet, dass A die Segmente nacheinander und kontinuierlich herauspumpen kann.
3.18
Abbildung 3.31 zeigt, dass TCP wartet, bis es drei Duplikat-ACKs empfangen hat,
bevor es ein Fast-Retransmit durchführt. Warum haben sich die TCP-Designer
Ihrer Meinung nach dafür entschieden, nach dem ersten Duplikat-ACK für ein
Segment kein Fast-Retransmit durchzuführen?
3.19
Betrachten Sie die TCP-Prozedur für die Schätzung der RTT. Angenommen,
x = 0,1, SampleRTT1 die letzte Muster-RTT, SampleRTT2 die vorletzte Muster-RTT
usw.
c. Gehen Sie für eine bestimmte TCP-Verbindung davon aus, dass vier Bestätigungen mit entsprechenden Muster-RTTs – SampleRTT4, SampleRTT3,
SampleRTT2 und SampleRTT1 – zurückgegeben wurden. Drücken Sie EstimatedRTT in Bezug zu den vier Muster-RTTs aus.
d. Generalisieren Sie Ihre Formel für n Muster-Roundtrip-Zeiten.
e. Es sei gegeben, dass n bei der Formel in Teil b. sich Unendlich nähert. Erklären Sie, warum man diese Art der Ermittlung des Durchschnitts als »Exponential Weighted Moving Average« (EWMA) bezeichnet.
3.20
In Abbildung 3.51 ist die Konvergenz des TCP-Algorithmus Additive-Increase/Multiplicative-Decrease dargestellt. Es wird angenommen, dass TCP statt eines Multiplicative-Decrease die Fenstergröße um einen konstanten Wert erhöht. Würde
das resultierende Additive-Increase/Additive-Decrease auf einen anteilig gleichen
Algorithmus konvergieren? Erklären Sie Ihre Antwort mit Hilfe eines Diagramms,
ähnlich Abbildung 3.51.
3.21
Denken Sie an das idealisierte Modell für die Dauerzustandsdynamik von TCP. In
der Zeitperiode, ab der die Fenstergröße der Verbindung von (W·MSS)/2 bis
W·MSS schwankt, geht nur ein Paket verloren (ganz am Ende der Periode).
a. Weisen Sie nach, dass die Verlustrate wie folgt aussieht:
1
L = Verlustrate = ------------------------3 2 3
--- w + --- w
8
4
b. Verwenden Sie das obige Ergebnis, um aufzuzeigen, dass bei einer Verlustrate
L einer Verbindung die durchschnittliche Bandbreite dieser Verbindung ungefähr wie folgt angegeben werden kann:
~ 1,22 · MSS/[RTT · sqrt(L)]
3.22
Sie möchten ein Objekt mit der Größe O = 100 Kbyte vom Server zum Client
senden. Seien S = 536 Byte und RTT = 100 ms. Gehen Sie davon aus, dass
das Transportprotokoll statische Fenster mit der Fenstergröße W verwendet.
a. Ermitteln Sie die mögliche minimale Latenz für eine Übertragungsrate von 28
Kbps. Bestimmen Sie die minimale Fenstergröße, die diese Latenz erreicht.
b. Wiederholen Sie a. für 100 Kbps.
c. Wiederholen Sie a. für 1 Mbps.
d. Wiederholen Sie a. für 10 Mbps.
3.23
Angenommen, TCP würde sein Überlastfenster während des Slow-Starts um Zwei
statt Eins für jede empfangene Bestätigung erhöhen. Folglich besteht das erste
➔
Übungen
➔
267
Fenster aus einem Segment, das zweite aus drei Segmenten, das dritte aus neun
Segmenten usw. Lösen Sie für diese Slow-Start-Prozedur folgende Aufgaben:
a. Drücken Sie K in Bezug zu O und S aus.
b. Drücken Sie Q in Bezug zu RTT, S und R aus.
c. Drücken Sie die Latenz in Bezug zu P = min(K – 1, Q), O, R und RTT aus.
3.24
Betrachten Sie den Fall von RTT = 1 Sekunde und O = 100 Kbyte. Arbeiten Sie
ein Diagramm (ähnlich denen in Abschnitt 3.5.2) aus, das die minimale Latenz
(O/R + 2 RTT) mit der Latenz beim Slow-Start für R = 28 Kbps, 100 Kbps, 1
Mbps und 10 Mbps vergleicht.
3.25
Richtig oder falsch?
a. Wenn eine Web-Seite aus genau einem Objekt besteht, dann haben nicht
persistente und persistente Verbindungen hinsichtlich der Reaktionszeit
genau die gleiche Leistung.
b. Betrachten Sie das Senden eines Objekts mit der Größe O vom Server zu
einem Browser über TCP. Wenn O > S, wobei S die maximale Segmentgröße
(MSS) ist, dann wartet der Server mindestens einmal.
c. Angenommen, eine Web-Seite besteht aus zehn Objekten mit jeweils einer
Größe von O Bit. Beim persistenten HTTP ist der RTT-Anteil an der Reaktionszeit 20 RTT.
d. Angenommen, eine Web-Seite besteht aus zehn Objekten mit jeweils einer
Größe von O Bit. Beim nicht persistenten HTTP mit fünf parallelen Verbindungen ist der RTT-Anteil an der Reaktionszeit 12 RTT.
3.26
Die Analyse für dynamische Fenster im Textteil basiert auf der Annahme, dass es
eine Verbindungsleitung zwischen Server und Client gibt. Erstellen Sie die Analyse
neu für T Verbindungsleitungen zwischen Server und Client. Gehen Sie davon aus,
dass das Netzwerk nicht überlastet ist, so dass Pakete keinen Warteschlangenverzögerungen ausgesetzt sind. Die Pakete müssen allerdings eine Store-and-Forward-Verzögerung über sich ergehen lassen. Die Definition von RTT entspircht der
im Abschnitt über die TCP-Überlastkontrolle enthaltenen. (Hinweis: Die Zeit, die
zwischen dem Absenden des ersten Segments durch den Server und dem Empfang der Bestätigung vegeht, ist TS/R + RTT.)
3.27
Erinnern Sie sich an die Diskussion über die Reaktionszeit für eine Web-Seite in
Abschnitt 3.7.3. Ermitteln Sie für den Fall nicht persistenter Verbindungen einen
allgemeinen Ausdruck für den Anteil an der Reaktionszeit, der auf den TCP-SlowStart zurückzuführen ist.
3.28
Beim persistenten HTTP werden alle Objekte über die gleiche TCP-Verbindung
gesendet. Wie in Kapitel 2 beschrieben wurde, ist eine der Motivationen für persistentes HTTP (mit Pipelining), die Auswirkungen des Verbindungsaufbaus und
des Slow-Starts von TCP auf die Reaktionszeit für eine Web-Seite zu verringern. In
dieser Übung untersuchen wir die Reaktionszeit für persistentes HTTP. Angenommen, der Client fordert alle Bilder gleichzeitig an, aber erst, wenn er die ganze
HTML-Basisseite erhalten hat. Es seien M + 1 die Anzahl der Objekte und O die
Größe jedes Objekts.
a. Erklären Sie, dass die Reaktionszeit aufgrund des Slow-Starts die Form
(M + 1)O/R + 3RTT + Latenz annimmt. Vergleichen Sie die Verteilung der
RTTs in diesem Ausdruck mit der beim nicht persistenten HTTP.
➔
Kapitel 3 – Transportschicht
268
➔
b. Nehmen Sie an, dass K = log2(O/S + 1) eine Ganzzahl ist; folglich überträgt
das letzte Fenster der HTML-Basisdatei die Segmentmenge eines ganzen
Fensters, d. h., Fenster K überträgt 2K–1 Segmente. P’ = min{Q, K’ – 1} und
K’ =
O
log 2  ( M + 1 ) ---+1


S
Beachten Sie, dass K’ die Anzahl der Fenster ist, die ein Objekt mit der Größe
(M + 1)O abdeckt, während P’ die Anzahl der Warteperioden ist, wenn das
große Objekt über eine einzige TCP-Verbindung gesendet wird. Nehmen wir
(falsch!) an, dass der Server die Bilder senden kann, ohne auf die formelle
Anfrage für die Bilder vom Client warten zu müssen. Weisen Sie nach, dass
die Reaktionszeit diejenige für das Senden eines großen Objekts mit der
Größe (M + 1)O ist:
Ungefähre Reaktionszeit
( M + 1 )O
= 2RTT + ------------------------ +
R
S
S
P’
P’ RTT + --- – ( 2 – 1 ) --R
R
c. Die tatsächliche Reaktionszeit beim persistenten HTTP ist etwas größer als
die Annäherung. Der Grund ist, dass der Server auf eine Anfrage für die Bilder
warten muss, bevor er sie sendet. Die Wartezeit zwischen dem K-ten Fenster
und Fenster (K + 1) ist nicht [S/R + 2K – 1(S/R]+, sondern RTT. Beweisen Sie
Folgendes:
S
( M + 1 )O
Reaktionszeit = 3RTT + ------------------------ + P’ RTT + --- –
R
R
S
S
S K–1
P’
( 2 – 1 ) --- – --- + RTT – ---2
R
R
R
+
3.29
Betrachten Sie das Szenario mit RTT = 100 ms, O = 5 Kbyte, S = 536 Byte und
M = 10. Erstellen Sie ein Diagramm, das die Reaktionszeiten für nicht persistente und persistente Verbindungen mit 28 Kbps, 100 Kbps, 1 Mbps und 10
Mbps vergleicht. Beachten Sie, dass persistentes HTTP bei allen genannten Übertragungsraten außer 28 Kbps eine wesentlich niedrigere Reaktionszeit als nicht
persistentes HTTP hat.
3.30
Wiederholen Sie die obige Übung für den Fall von RTT = 1 s, O = 5 Kbyte, S =
536 Byte und M = 10. Beachten Sie bei diesen Parametern, dass persistentes
HTTP bei allen genannten Übertragungsraten eine beträchtlich niedrigere Reaktionszeit als nicht persistentes HTTP aufweist.
3.31
Betrachten Sie nicht persistentes HTTP mit parallelen TCP-Verbindungen. Browser
arbeiten normalerweise in diesem Modus, wenn sie HTTP/1.0 verwenden. Sei X die
maximale Anzahl an parallelen Verbindungen, die der Client (Browser) gleichzeitig
öffnen kann. In diesem Modus benutzt der Client zuerst eine TCP-Verbindung, um
die HTML-Basisdatei zu holen. Nach Empfang der HTML-Basisdatei baut der Client
M/X Gruppen von TCP-Verbindungen auf, wobei jede Gruppe X parallele Verbindungen hat. Erklären Sie, dass die gesamte Reaktionszeit folgende Form annimmt:
➔
Übungen und Programmieraufgaben
➔
Reaktionszeit = (M + 1)O/R + 2(M/X + 1) RTT +
Latenz aufgrund der Slow-Start-Wartezeit
Vergleichen Sie die Verteilung des Terms in Bezug zu RTT bei persistenten und
nicht persistenten (nicht parallelen) Verbindungen.
DISKUSSIONSFRAGEN
3.1
Betrachten Sie Streaming von gespeichertem Audio. Ist es sinnvoller, die Anwendung über UDP oder TCP auszuführen? Welches der beiden Protokolle wird von
RealNetworks benutzt? Warum? Sind Ihnen weitere Produkte für das Streaming
von gespeichertem Audio bekannt? Welches Transportprotokoll verwenden sie und
warum?
PROGRAMMIERAUFGABEN
In dieser Programmieraufgabe werden Sie Code für die Sende- und Empfangsseite auf
Transportebene für die Implementierung eines einfachen zuverlässigen Datentransferprotokolls – entweder das Alternating-Bit-Protokoll oder ein Go-Back-N-Protokoll – schreiben.
Dies sollte Spaß machen, weil sich Ihre Implementierung kaum davon unterscheiden
wird, was für die wirkliche Welt nötig wäre.
Da Sie wahrscheinlich nicht über Standalone-Rechner (mit einem Betriebssystem, das
Sie modifizieren können) verfügen, muss Ihr Code in einer simulierten Hardware/Software-Umgebung ausgeführt werden. Die für Ihre Routinen bereitgestellte Programmieroberfläche (d. h. der Code, der Ihre Instanzen von oben (von Schicht 5) und von unten
(von Schicht 3) aufrufen würde), ähnelt stark einer echten Unix-Umgebung. (Tatsächlich
sind die in dieser Programmieraufgabe beschriebenen Softwareoberflächen viel realistischer als die Sender und Empfänger mit unendlicher Schleife, die man in vielen Lehrbüchern findet.) Außerdem wird das Stoppen und Starten von Timern simuliert und TimerInterrupts werden Ihre Timer-Behandlungsroutine aktivieren.
Ausführliche Einzelheiten zu dieser Programmierübung sowie einen C-Code, den Sie für
das Erstellen der simulierten Hardware/Software-Umgebung benötigen, finden Sie auf
http://www.awl.com/kurose-ross.
269
Kapitel 3 – Transportschicht
270
INTERVIEW
Sally Floyd
Sally Floyd ist Forscherin am AT&T Center for Internet Research am ICSI-Institut
(ACIRI), das sich mit Internet- und Vernetzungsfragen beschäftigt. Sie ist in der
Industrie durch ihre Internet-Protokolldesigns, insbesondere zuverlässiges Multicast,
Überlastkontrolle (TCP), Paket-Scheduling (RED) sowie Protokollanalyse bekannt.
Sally erhielt ihren B.A. in Soziologie und ihren M.S. und Ph.D. in Computerwissenschaft an der University of California, Berkeley.
õ Was hat Sie dazu bewegt, Computerwissenschaften zu studieren? Was hat Ihr
Interesse an diesem Gebiet geweckt?
Nach meinem Soziologiestudium musste ich mir Gedanken darüber machen, wie ich
meinen Lebensunterhalt verdienen würde. Es ergab sich für mich eine zweijährige
Assistententätigkeit im Elektronikforschungsbereich am örtlichen College, wonach
ich dann schließlich zehn Jahre im Bereich der Elektronik und Computerwissenschaften tätig war. Das umfasste acht Jahre als Computer Systems Engineer für die Computer, die die BART-Züge (Bay Area Rapid Transit) steuern. Später entschloss ich
mich zum Studium einer mehr formellen Computerwissenschaft und bewarb mich
an der Graduate School beim Computer Science Department der UC Berkeley.
õ Was hat Sie dazu bewegt, sich auf Vernetzung zu spezialisieren?
An der Graduate School entstand mein Interesse an theoretischer Computerwissenschaft. Ich arbeitete zuerst an der Wahrscheinlichkeitsanalyse von Algorithmen und
später an Computerlerntheorie. Einen Tag im Monat arbeitete ich außerdem am LBL
(Lawrence Berkeley Laboratory). Mein Büro befand sich genau gegenüber von dem
von Van Jacobson, der zur damaligen Zeit an TCP-Überlastkontrollalgorithmen arbeitete. Van fragte mich, ob ich den Sommer über an Analysen von Algorithmen für ein
netzwerkbezogenes Problem, das mit der unerwünschten Synchronisation periodischer Routing-Nachrichten zu tun hatte, arbeiten möchte. Ich fand das sehr interessant und nahm das Angebot an.
Nach meinem Diplom bot mir Van Jacobson eine Vollzeitbeschäftigung im Vernetzungsbereich an. Ich hatte eigentlich nicht vor, so lange im Vernetzungsbereich zu
arbeiten. Ich finde Netzwerkforschung inzwischen aber erfüllender als theoretische
Computerwissenschaft. Ich fühle mich wohler mit praxisnaher Arbeit, deren Ergebnisse greifbarer sind.
õ Was war Ihre erste Stelle in der Computerindustrie? Welches Aufgabengebiet
hatten Sie?
Mein erster Computerjob war bei BART (Bay Area Rapid Transit) von 1975 bis 1982.
Ich arbeitete dort an den Computern, die die BART-Züge steuern. Ich begann als Wartungstechnikerin für die verschiedenen dezentralen Computersysteme des BARTSystems.
Dies umfasste ein zentrales Rechensystem und ein verteiltes Minicomputersystem
für die Kontrolle der Zugbewegung, ein System aus DEC-Computern für die Anzeige
von Werbungen und Fahrplänen sowie ein System aus Modcomp-Computern für die
Erfassung von Informationen von den Fahrkartenschaltern. Meine letzten Jahre bei ➔
Interview
➔ BART arbeitete ich an einem gemeinsamen BART/LBL-Projekt für die Ablösung der
alternden BART-Computeranlagen.
õ Was ist der interessanteste Teil Ihres Aufgabenbereichs?
Der interessanteste Teil ist sicherlich die eigentliche Forschungsarbeit. Derzeit bedeutet das die Entwicklung und Erforschung eines neuen Mechanismus für gleichungsbasierte Ende-zu-Ende-Überlastkontrolle. Damit soll TCP nicht etwa abgelöst werden. Vielmehr könnte man damit für Unicast-Verkehr, wie beispielsweise einen
ratenadaptiven Echtzeitverkehr, hohe Ratenänderungen bzw. die Halbierung der
Senderate aufgrund eines einzigen Paketverlusts vermeiden. Eine auf Gleichungen
basierte Überlastkontrolle ist auch als potenzielle Grundlage für Multicast interessant. Weitere Informationen hierüber befinden sich auf der Web-Seite unter http://
www.psc.edu/networking/tcp_friendly.html.
õ Wie sieht Ihrer Meinung nach die Zukunft der Computernetzwerke/
des Internets aus?
Eine Möglichkeit ist, dass die im Internet-Verkehr häufig vorkommende Überlastung
abgebaut werden kann, wenn gebührenbasierte Mechanismen eingeführt werden
und die verfügbare Bandbreite schneller als die Nachfrage steigt. Meiner Meinung
nach zeichnet sich ein Trend hin zu geringeren Überlastungen ab, wobei mittelfristig
ein gelegentlicher Kollaps durch Überlastungen nicht auszuschließen ist.
Die Zukunft des Internets oder der Internet-Architektur ist mir überhaupt nicht
klar. Es gibt viele Faktoren, die zu einem schnellen Wandel beitragen, so dass es
schwierig ist vorauszusagen, wie sich das Internet oder die Internet-Architektur weiterentwickeln wird, und schon gar nicht, wie erfolgreich diese Weiterentwicklung
angesichts der vielen potenziellen Fallen sein wird.
õ Durch welche Leute wurden Sie beruflich inspiriert?
Richard Karp, mein Betreuer an der Graduate School, zeigte mir im Wesentlichen, wie
man richtige Forschungsarbeit betreibt, Meinem »Gruppenleiter« bei LBL, Van Jacobson, verdanke ich mein Interesse an Vernetzung und meine Kenntnisse der InternetInfrastruktur. Dave Clark inspirierte mich durch seine klare Sicht der Internet-Architektur und seine Rolle in der Entwicklung dieser Architektur im Rahmen von Forschungsarbeiten, Veröffentlichungen und Teilnahmen an der IETF und anderen
öffentlichen Foren. Deborah Estrin hat mich durch ihren Weitblick und ihre Fähigkeit,
kluge Entscheidungen über Projekte zu treffen, inspiriert.
Einer der Gründe, warum ich meinen Arbeitsbereich in den letzten zehn Jahren
auf Netzwerkforschung verlagert habe, ist der, dass es so viele Leute gibt, die auf diesem Gebiet arbeiten, die ich mag und respektiere und die mich anregen. Sie sind klug,
tüchtig und stark in der Entwicklung des Internets engagiert; sie leisten eindrucksvolle Arbeit und sind angenehme Kollegen und nette Kumpel, mit denen man auch
mal ein Bier trinken kann, auch wenn man beruflich nicht unbedingt immer gleicher
Meinung ist.
271
Herunterladen