Zurück zum Inhaltsverzeichnis des Manuskripts verteilte Systeme

4.4.2 Bestätigungen

Sendewiederholungen

TCP sichert den zu übertragenden Bytestrom durch Bestätigungen, die in der englischsprachigen Literatur acknowledgements genannt werden. Wird ein Segment gesendet, setzt die sendende TCP-Station eine Uhr, man spricht auch im Deutschen von einem Timeout. Ist die Uhr abgelaufen, ohne dass eine Bestätigung des Erhalts der Datenbytes dieses Segments eingegangen ist, dann wird seine Übertragung wiederholt. Das heißt, dass eine empfangende TCP-Station in der Lage sein muss, mehrfach empfangene Segmente als solche zu erkennen und (bis auf eines) zu verwerfen.

Die Zeitdauer für den Timeout muss sorgfältig gewählt werden. Ist sie zu klein, gibt es viele (überflüssige) Wiederholungen korrekt empfangener Segmente. Wird sie zu groß gewählt, dauert es unnötig lange, bis ein fehlerhaft übertragenes Segment wiederholt wird. Die Betrachtungen dazu sind komplex und haben sich in einem eigenen RFC (6298) niedergeschlagen. Hier auf sie einzugehen würde den Rahmen dieser Grundlagenveranstaltung sprengen.

Bezüglich der maximalen Anzahl an Wiederholungen bei einem ausstehenden Timeout verweist Stevens in seinem Lehrbuch

W. Richard Stevens TCP/IP Illustrated Volume 1 The Protocols Addison-Wesley 1994

auf der Seite 298 auf ein Experiment mit der bei ihm installierten TCP-Software, die nach 12 vergeblichen Versuchen, ein Segment zuzustellen, die Übertragung der Anwendungsbytefolge abgebrochen und den Partner zu einem Neustart aufgefordert hat.

Bestätigungsnummern

Bestätigungen beziehen sich auf die bereits korrekt erhaltenen Bytes und nicht auf Segmente. Das heißt, dass Bestätigungen Bytenummern (Acknowledgement Numbers) und keine Segmentnummern sind. Im TCP-Header gibt es ein 32 Bits großes Datenfeld, das die Bestätigungsnummer aufnimmt. Es wird ack genannt. Sein Wert ist - genau wie eine Sequenznummer - eine positive, ganze Zahl aus dem Bereich zwischen 0 und 232-1. Der Wert dieses ack-Feldes wird folgendermaßen interpretiert: Ist die Zahl a der Wert dieses Feldes (ack=a), dann sind alle Bytes, beginnend bei der isn des Senders bis einschließlich des Bytes mit der Nummer a-1 korrekt empfangen worden, und als nächstes wird ein Segment erwartet, dessen erstes Byte die Sequenznummer a trägt.

Angenommen, ein zu übertragender Datenstrom beginnt mit der zufälligen Bytenummer 9. Dann bedeutet eine Bestätigung mit der Zahl 471, dass bereits alle Bytes von 9 bis 470 (einschließlich) korrekt empfangen worden sind und das Byte mit der Nummer 471 im nächsten Segment als erstes Datenbyte erwartet wird.

Bestätigungen (Bestätigungsnummern) werden wie Anwendungsdaten durch Segmente transportiert, allerdings nicht in deren Datenteil sondern im Segmentkopf. Allerdings muss dem Empfänger der Bestätigung mitgeteilt werden, dass im Bestätigungsfeld tatsächlich eine Bestätigung transportiert wird, denn da steht ja immer irgendein Bitmuster. Dazu gibt es im Segmentkopf ein Flag (ein Bit), das vom Sender gesetzt (auf 1) werden muss, wenn er eine Bestätigung sendet. Ist dieses Flag gesetzt, dann wird dies in der linearisierten Schreibweise eines Segments als ACK vermerkt.

Ein Segment, das eine Bestätigung transportiert, kann zusammen mit der Bestätigung auch Anwendungsdaten transportieren, denn TCP realisiert eine Vollduplex-Datenübertragung. Ein typisches Segment hat dann die folgende Form:

[ seq=539, ACK, ack=471, 8 bytes data ]

Dieses Segment transportiert Anwendungsdaten und eine Bestätigung. Die transportierten acht Anwendungsdatenbytes haben die Sequenznummern von 539 bis 546. Bestätigt wird der korrekte Empfang von Anwendungsdatenbytes bis einschließlich der Sequenznummer 470, beginnend bei der isn des Senders.

Bestimmung der Bestätigungsnummer

Der Empfänger eines Segments kann die Bestätigungsnummer, die er bei korrektem Empfang des bisher gesendeten Bytestroms zurückzusenden muss, aus der Sequenznummer des eingegangenen Segments und der Anzahl der darin enthaltenen Anwendungsdatenbytes bestimmen, denn es gilt:

ack = seq + anz

Dabei steht anz für die Anzahl der Bytes an Anwendungsdaten aus dem gerade empfangenen Segment.

Ausnahmen bei der ack-Wert-Bestimmung

Die Formel ack=seq+anz gilt auch für den Fall, dass ein Segment keine Anwendungsdaten transportiert. Dann ist anz gleich Null, und die Formel verkürzt sich zu:

ack = seq + anz ack = seq + 0 ack = seq

Allerdings gibt es zwei spezielle Segmentarten, die beide keine Anwendungsdaten transportieren dürfen, bei denen also anz=0 ist, und bei denen eine andere Berechnung der Bestätigungsnummer vorgenommen werden muss. Neben dem bereits bekannten ACK-Flag gibt es im Segmentkopf unter anderem auch noch ein SYN- und ein FIN-Flag. Ist eines der beiden gesetzt (auf 1), dann muss die Bestätigungsnummer mit der Formel

ack = seq + 1

berechnet werden. Die Gründe dafür werden in den nächsten beiden Absätzen gezeigt.

SYN-Segmente

Will ein TCP-System namens A eine Verbindung zu einem TCP-System namens B herstellen, dann sendet es zuerst ein Segment ohne Daten und mit gesetztem SYN-Flag an B. Solche Segmente heißen kurz SYN-Segmente. Dabei steht SYN für die Aufforderung, dass B mit A in Verbindung treten möge (dass B sich mit A synchronisieren möge). In diesem ersten Segment setzt A die Sequenznummer seq auf die von ihm zufällig gewählte Anfangssequenznummer, die hier isnA heißen soll, und sendet:

A -> B [ SYN, seq=isnA, no data ]

Das TCP-System B bestätigt dieses Segment. Es berechnet die Bestätigungsnummer, setzt sie ein und setzt das ACK-Flag. Ist B willig, die Verbindung einzugehen, dann setzt es im Bestätigungssegment seinerseits das SYN-Flag und belegt die Sequenznummer mit seiner zufällig gewählten Anfangssegmentnummer, hier mit isnB. B antwortet also mit:

B -> A [ ACK, ack=isnA+1, SYN, seq=isnB, no data ]

Man beachte, dass beim Setzen der Bestätigungsnummer, wegen des gesetzten SYN-Flags in dem empfangenen Segment, von der Formel ack=seq+anz (ack=seq+0) abgewichen wurde. Wäre dies nicht geschehen, dann hätte das Bestätigungssegment folgendermaßen gelautet:

B -> A [ ACK, ack=isnA, SYN, seq=isnB, no data ]

Damit aber würde B den korrekten Erhalt aller Datenbytes von isnA bis isnA-1 bestätigen. Das wären wegen der Modulo-Rechnung bei den Sequenznummern 232-1 Bytes, die bestätigt würden. Das ist offensichtlich falsch. Durch die Berechnung

ack = seq +1

wird dieses Problem beseitigt. B bestätigt damit den korrekten Erhalt aller Bytes an Anwendungsdaten von isnA bis isnA. Das sind 0 Bytes, was die Situation richtig beschreibt, allerdings auf Kosten des Verbrauchs einer Sequenznummer.

FIN-Segmente

Mit einem Segment, bei dem das FIN-Flag gesetzt ist und das keine Anwendungsdaten transportieren darf ( anz=0), erklärt ein TCP-System, hier wieder A genannt, seinem Partner, der B heißen soll, dass es ab sofort keine Anwendungsdaten mehr übertragen wird.

A -> B [ FIN, no data ]

Um für diesen Fall das Problem bei der Bestimmung der Bestätigungsnummer herauszuarbeiten, soll folgende Situation betrachtet werden:

Man betrachte den folgendenen Segmentaustausch:

A sendet seine letzten 5 Datenbytes A -> B [ seq=3647, 5 bytes data ] B bestätigt B -> A [ ACK, ack=3652, no data ] A hat keine Daten mehr A -> B [ FIN, seq=3652, no data ] B bestätigt B -> A [ ACK, ack=3653, no data ]

Man beachte die Reaktion von B auf den Eingang eines FIN-Segments. B ist bei der Berechnung seines ACK-Wertes (ack=3653) von der Formel ack=seq+anz (ack=seq+0) abgewichen und hat statt dessen ack=seq+1 gerechnet. Hätte B dies nicht getan, dann hätte sein Bestätigungssegment auf das FIN-Segment gelautet:

B -> A [ ACK, ack=3652, no data ]

Das aber würde dazu führen, dass A zwei Segmente erhält (das zweite und vierte in der obigen Auflistung), die es nicht voneinander unterscheiden und deshalb nicht eindeutig zuordnen kann. Auch hier wird wie bei den SYN-Segmenten die Eindeutigkeit auf Kosten des Verbrauchs einer Sequenznummer erreicht.



Zurück zum Inhaltsverzeichnis des Manuskripts verteilte Systeme