Transmission Control Protocol (TCP)

Das Transmission Control Protocol (TCP) ist eine Vereinbarung (Protokoll) darüber, auf welche Art und Weise Daten zwischen Computern ausgetauscht werden sollen. Alle Betriebssysteme moderner Computer beherrschen TCP und nutzen es für den Datenaustausch mit anderen Rechnern. Das Protokoll ist ein zuverlässiges, verbindungsorientiertes Transportprotokoll in Computernetzwerken. Es ist Teil der Internetprotokollfamilie, der Grundlage des Internets. Entwickelt wurde TCP von Robert E. Kahn und Vinton G. Cerf. Ihre Forschungsarbeit, die sie bereits im Jahre 1973 begannen, zog sich über mehrere Jahre hin. Deshalb erfolgte die erste Standardisierung von TCP erst im Jahre 1981 als RFC 793. Danach gab es viele Erweiterungen, die bis heute in neuen RFCs (einer Reihe von technischen und organisatorischen Dokumenten zum Internet) spezifiziert werden und alle zu TCP gehören. Im Unterschied zum verbindungslosen UDP (User Datagram Protocol) stellt TCP einen virtuellen Kanal zwischen zwei Endpunkten einer Netzwerkverbindung (Sockets) her. Auf diesem Kanal können in beide Richtungen Daten übertragen werden. TCP setzt in den meisten Fällen auf das IP (Internet-Protokoll) auf, weshalb häufig (und oft nicht ganz korrekt) auch vom TCP/IP-Protokoll die Rede ist. Es ist in Schicht 4 des OSI-Referenzmodells angesiedelt. Aufgrund seiner vielen angenehmen Eigenschaften (Datenverluste werden erkannt und automatisch behoben, Datenübertragung ist in beiden Richtungen möglich, Netzwerküberlastung wird verhindert usw.) ist TCP ein sehr weit verbreitetes Protokoll zur Datenübertragung. Beispielsweise wird TCP als (fast) ausschließliches Transportmedium für das WWW, E-Mail, Daten in Peer-to-Peer-Netzwerken und für viele andere populäre Netzwerkdienste verwendet.

Allgemeines

TCP ist im Prinzip eine Ende-zu-Ende-Verbindung in Vollduplex, welche die Übertragung der Informationen in beide Richtungen zu gleicher Zeit zulässt. Diese Verbindung kann in zwei Halbduplexverbindungen eingeteilt werden, bei denen Informationen in beide Richtungen (allerdings nicht gleichzeitig) fließen können. Die Daten in Gegenrichtung können dabei zusätzliche Steuerungsinformationen enthalten. Die Verwaltung (management) dieser Verbindung sowie die Datenübertragung werden von der TCP-Software übernommen. Die TCP-Software ist eine Funktionssammlung und (je nach Betriebssystem unterschiedlich) bei Linux auch im Betriebssystemkern, dem Linux-Kernel, angesiedelt. Anwendungen, die diese Software häufig nutzen, sind zum Beispiel Webbrowser und Webserver. Jede TCP-Verbindung wird eindeutig durch zwei Endpunkte identifiziert. Ein Endpunkt stellt ein geordnetes Paar dar, bestehend aus IP-Adresse und Port. Ein solches Paar bildet eine bi-direktionale Software-Schnittstelle und wird auch als Socket bezeichnet. Mit Hilfe der IP-Adressen werden die an der Verbindung beteiligten Rechner identifiziert; mit Hilfe der Ports werden dann auf den beiden beteiligten Rechnern die beiden miteinander kommunizierenden Programme identifiziert. Durch die Verwendung von Portnummern auf beiden Seiten der Verbindung ist es beispielsweise möglich, dass ein Webserver auf einem Port (normalerweise Port 80) gleichzeitig mehrere Verbindungen zu einem anderen Rechner geöffnet hat. Ports sind 16-Bit-Zahlen (Portnummern) und reichen von 0 bis 65535. Ports von 0 bis 1023 sind reserviert und werden von der IANA vergeben, z.B. ist Port 80 für das im WWW verwendete HTTP-Protokoll reserviert. Allerdings ist das Benutzen der vordefinierten Ports nicht bindend. So kann jeder Administrator beispielsweise einen FTP-Server (normalerweise Port 21) auch auf einem beliebigen anderen Port laufen lassen.

Verbindungsaufbau und -abbau

Ein Webserver, der seinen Dienst anbietet, generiert einen Endpunkt mit dem Port und seiner Adresse. Dies wird als passive open oder auch als listen bezeichnet. Will ein Client eine Verbindung aufbauen, generiert er einen eigenen Endpunkt aus seiner Rechneradresse und einer noch freien Portnummer. Mit Hilfe eines ihm bekannten Ports und der Adresse des Servers kann dann eine Verbindung aufgebaut werden. Während der Datenübertragungsphase (active open) sind die Rollen von Client und Server (aus TCP-Sicht) vollkommen symmetrisch. Insbesondere kann jeder der beiden beteiligten Rechner einen Verbindungsabbau einleiten. Während des Abbaus kann die Gegenseite noch Daten übertragen, die Verbindung kann also halb-offen sein.

Der Drei-Wege-Handshake

Der Drei-Wege-Handshake ist die Bezeichnung für ein bestimmtes Verfahren, um eine in Bezug auf Übertragungsverluste sichere Datenübertragung zwischen zwei Instanzen zu ermöglichen. Obwohl überwiegend in der Netzwerktechnik verwendet, ist der Drei-Wege-Handshake nicht auf diese beschränkt.

Verbindungsaufbau

Beim Aufbau einer TCP-Verbindung kommt der sogenannte Drei-Wege-Handshake zum Einsatz. Der Rechner, der die Verbindung aufbauen will, sendet dem anderen ein SYN-Paket (von engl. synchronize) mit einer Sequenznummer x. Die Sequenznummern sind dabei für die Sicherstellung einer vollständigen Übertragung in der richtigen Reihenfolge und ohne Duplikate wichtig. Es handelt sich also um ein Paket, dessen SYN-Bit im Paketkopf gesetzt ist (siehe TCP-Header). Die Start-Sequenznummer ist eine beliebige Zahl, deren Generierung von der jeweiligen TCP-Implementierung abhängig ist. Sie sollte jedoch möglichst zufällig sein, um Sicherheitsrisiken zu vermeiden. Die Gegenstelle (siehe Skizze) empfängt das Paket und sendet in einem eigenen SYN-Paket im Gegenzug ihre Start-Sequenznummer y (die ebenfalls beliebig und unabhängig von der Start-Sequenznummer der Gegenstelle ist). Zugleich bestätigt sie den Erhalt des ersten SYN-Pakets, indem sie die Sequenznummer x um eins erhöht und im ACK-Teil (von engl. acknowledgment = Bestätigung) des Headers zurückschickt. Der Client bestätigt zuletzt den Erhalt des SYN/ACK-Pakets durch das Senden eines eigenen ACK-Pakets mit der Sequenznummer y+1. Dieser Vorgang wird auch als Forward Acknowledgement bezeichnet. Außerdem sendet der Client den Wert x+1 aus Sicherheitsgründen ebenso zurück. Dieses ACK-Segment erhält der Server, das ACK-Segment ist durch das gesetzte ACK-Flag gekennzeichnet. Die Verbindung ist damit aufgebaut.

Verbindungsabbau

Der geregelte Verbindungsabbau erfolgt ähnlich. Statt des SYN-Bits kommt das FIN-Bit (von engl. finish = Ende, Abschluss) zum Einsatz, welches anzeigt, dass keine Daten mehr vom Sender kommen. Der Erhalt des Pakets wird wiederum mittels ACK bestätigt. Der Empfänger des FIN-Pakets sendet zuletzt seinerseits ein FIN-Paket, das ihm ebenfalls bestätigt wird. Obwohl eigentlich vier Wege genutzt werden, handelt es sich beim Verbindungsabbau auch um einen Drei-Wege-Handshake, da die ACK- und FIN-Operationen vom Server zum Client als ein Weg gewertet werden. Zudem ist ein verkürztes Verfahren möglich, bei dem FIN und ACK genau wie beim Verbindungsaufbau im selben Paket untergebracht werden. Die maximum segment lifetime (MSL) ist die maximale Zeit, die ein Segment im Netzwerk verbringen kann, bevor es verworfen wird. Nach dem Senden des letzten ACKs wechselt der Client in einen zwei MSL andauernden Wartezustand (Waitstate), in dem alle verspäteten Segmente verworfen werden. Dadurch wird sichergestellt, dass keine verspäteten Segmente als Teil einer neuen Verbindung fehlinterpretiert werden. Außerdem wird eine korrekte Verbindungsterminierung sichergestellt. Geht ACK y+1 verloren, läuft beim Server der Timer ab, und das LAST_ACK Segment wird erneut übertragen.

Aufbau des TCP-Headers

Das TCP-Segment besteht immer aus zwei Teilen - dem Header und der Nutzlast (Payload). Die Nutzlast enthält die zu übertragenden Daten, die wiederum Protokollinformationen der Anwendungsschicht wie HTTP oder FTP entsprechen können. Der Header enthält für die Kommunikation erforderliche Daten sowie das Dateiformat beschreibende Information. Die Werte werden in network byte order (big endian) angegeben.

Datenübertragung

TCP- / IP-Paket-Größe

Ein TCP-Segment hat typischerweise eine Größe von 1500 Bytes. Es darf nur so groß sein, dass es in die darunterliegende Übertragungsschicht passt, das Internetprotokoll IP. Das IP-Paket ist theoretisch bis 65535 Bytes (64 Kilobyte) spezifiziert, wird aber selbst meist über Ethernet übertragen, und dort ist die Rahmengröße auf 1500 Bytes festgelegt. TCP und IP Protokoll definieren jeweils einen Header von 20 Bytes Größe. Für die Nutzdaten bleiben in einem TCP/IP-Paket also 1460 Bytes übrig. Da die meisten Internet-Anschlüsse DSL verwenden, gibt es dort noch das Point-to-Point Protocol (PPP) zwischen IP und Ethernet, was nochmal 8 Bytes für den PPP-Rahmen kostet. Dem TCP/IP-Paket verbleiben im Ethernet-Rahmen nur 1492 Bytes MTU, die Nutzdaten reduzieren sich auf insgesamt 1452 Bytes MSS. Dies entspricht einer Auslastung von 96,8 %.

Aufteilen der Anwendungsdaten auf TCP- / IP-Pakete

Empfänger und Sender einigen sich vor dem Datenaustausch über das Options-Feld auf die Größe der MSS. Die Anwendung, die Daten versenden möchte, beispielsweise ein Webserver, legt zum Beispiel einen 10 Kilobyte großen Datenblock im Puffer ab. Um so mit einem 1460 Byte großen Nutzdatenfeld 10 Kilobyte Daten zu versenden, teilt man die Daten auf mehrere Pakete auf, fügt einen TCP-Header hinzu und versendet die TCP-Segmente. Dieser Vorgang wird Segmentierung genannt. Im Puffer ist der Datenblock, dieser wird in fünf Segmente aufgeteilt. Jedes Segment erhält durch die TCP-Software einen TCP-Header. Drei TCP-Segmente wurden aktuell abgeschickt. Diese sind nicht notwendigerweise sortiert, da im Internet jedes TCP-Segment einen anderen Weg nehmen und es dadurch zu Verzögerungen kommen kann. Damit die TCP-Software im Empfänger die Segmente wieder sortieren kann, ist jedes Segment nummeriert (die Segmente werden sozusagen durchgezählt). Bei der Zuordnung der Segmente wird die Sequenznummer herangezogen. Der Empfänger muss diejenigen TCP-Segmente bestätigen, die einwandfrei (Prüfsumme ist in Ordnung) angekommen sind.

Flusssteuerung

Da die Anwendung Daten aus dem Puffer liest, ändert sich der Füllstand des Puffers ständig. Deshalb ist es notwendig, den Datenfluss dem Füllstand entsprechend zu steuern. Dies geschieht mit dem Sliding Window und dessen Größe. Die Größe dieser Windows wird ständig angepasst und der Process der Anpassung ist ein nicht zu unterschätzender Overhead in der Kommunikation von Sender und Empfänger.

Slow-Start

Zu Beginn einer Datenübertragung dient der Slow-Start-Algorithmus zur Bestimmung des congestion window (wörtlich: Überlast-Zeitfenster), um einer möglichen Überlastsituation vorzubeugen. Man möchte Staus vermeiden, und da die momentane Auslastung des Netzes nicht bekannt ist, wird mit zunächst kleinen Datenmengen begonnen. Der Algorithmus startet mit einem kleinen Zeitfenster von zwei MSS, in dem Datenpakete vom Sender zum Empfänger übertragen werden. Der Empfänger sendet nun eine Bestätigung (Acknowledgement, ACK) an den Sender zurück. Anschließend wird die Größe des congestion window um eine Segmentgröße erhöht. Für jede weitere Bestätigung wird dieses wieder um eine Segmentgröße erhöht, das Limit ist das vom Empfänger festgelegte Empfangsfenster. Das Wachstum des Fensters ist in der Regel exponentiell, also erst langsam, dann schnell; insofern kann der historisch bedingte Name Slow-Start fälschlicherweise ein langsames Wachstum suggerieren.

Überlastkontrolle

Gehen bei einer bestimmten Fenstergröße Pakete verloren, kann das festgestellt werden, wenn der Sender innerhalb einer bestimmten Zeit (Timeout) keine Bestätigung (ACK) erhält. Man muss davon ausgehen, dass das Paket aufgrund zu hoher Netzlast von einem Router im Netz verworfen wurde. Das heißt, der Puffer eines Routers ist vollgelaufen; es handelt sich hier sozusagen um einen Stau im Netz. Um diesen aufzulösen, müssen alle beteiligten Sender ihre Netzlast reduzieren. Wird ein Paketverlust festgestellt, so wählt man die Hälfte der noch unbestätigten Daten im Netz als geeignetes Zeitfenster für die Datenübertragung zwischen diesem Sender und diesem Empfänger über den benutzten Kanal. Das verlorene Paket wird erneut übertragen, für jede Bestätigung wird die Sendefenstergröße wieder um eine MSS erhöht wie beim Slow-Start. Fast-Retransmit und Fast-Recovery werden eingesetzt, um nach einem Paketverlust schneller auf die Stau-Situation zu reagieren. Dazu informiert ein Empfänger den Sender, wenn Pakete außer der Reihe ankommen und somit dazwischen ein Paketverlust vorliegt. Hierfür bestätigt der Empfänger das letzte korrekte Paket erneut für jedes weitere ankommende Paket außer der Reihe. Man spricht dabei von Dup-Acks (Duplicate acknowledgements). Der Sender bemerkt die duplizierten Bestätigungen, und nach dem dritten Duplikat sendet er sofort, vor Ablauf des Timers, das verlorene Paket erneut. Weil nicht auf den Ablauf des Timers gewartet werden muss, heißt das Prinzip Fast Retransmit. Die Dup-Acks sind auch Hinweise darauf, dass zwar ein Paketverlust stattfand, aber doch die folgenden Pakete angekommen sind. Deshalb wird das Sendefenster nach dem Fehler nur halbiert und nicht wie beim Timeout wieder mit Slow-Start begonnen. Zusätzlich kann das Sendefenster noch um die Anzahl der Dup-Acks erhöht werden, denn jedes steht für ein weiteres Paket, welches den Empfänger erreicht hat, wenn auch außer der Reihe. Da dadurch nach dem Fehler schneller wieder die volle Sendeleistung erreicht wird, nennt man das Prinzip Fast-Recovery (schnelles Erholen). Bis zum Erkennen des Paketverlustes wurden noch weitere Pakete bis zur Sendefenstergröße übertragen. Der Empfänger konnte diese nicht nutzen, weil ein Paket der Serie verloren ging, aber er kann sie im Puffer halten. Nach der Neuübertragung des verlorenen Pakets durch den Sender bestätigt der Empfänger mittels ACK und einer höheren Sequenznummer die nun vollständige Paketfolge. Das erspart dem Sender, alle nach dem Paketverlust übertragenen Pakete erneut zu übertragen, und er kann sofort mit ganz neuen Paketen fortfahren; man nennt das kumuliertes ACK. Selective ACKs werden genutzt, um noch mehr Kontrollinformationen über den Datenfluss vom Empfänger an den Sender zurückzuschicken. Dabei wird nach einem Paketverlust vom Empfänger im TCP-Optionsfeld ein zusätzlicher Header eingefügt, aus welchem der Sender genau ersehen kann, welche Pakete bereits angekommen sind und welche fehlen (im Gegensatz zu den standardmäßigen kumulativen ACKs von TCP). Als bestätigt gelten die Pakete auch weiterhin erst dann, wenn der Empfänger dem Sender ein ACK für die Pakete übermittelt hat.

Datenintegrität und Zuverlässigkeit

Im Gegensatz zum verbindungslosen UDP implementiert TCP einen bidirektionalen, byte-orientierten, zuverlässigen Datenstrom zwischen zwei Endpunkten. Das darunterliegende Protokoll (IP) ist paketorientiert, wobei Datenpakete verloren gehen können, in verkehrter Reihenfolge ankommen dürfen und sogar doppelt empfangen werden können. TCP wurde entwickelt, um mit der Unsicherheit der darunterliegenden Schichten umzugehen. Es prüft daher die Integrität der Daten mittels der Prüfsumme im Paketkopf und stellt die Reihenfolge durch Sequenznummern sicher. Der Sender wiederholt das Senden von Paketen, falls keine Bestätigung innerhalb einer bestimmten Zeitspanne (Timeout) eintrifft. Die Daten der Pakete werden beim Empfänger in einem Puffer in der richtigen Reihenfolge zu einem Datenstrom zusammengefügt und doppelte Pakete verworfen. Der Datentransfer kann selbstverständlich jederzeit nach dem Aufbau einer Verbindung gestört, verzögert oder ganz unterbrochen werden. Das Übertragungssystem läuft dann in einen Timeout. Der vorab getätigte Verbindungsaufbau stellt also keinerlei Gewähr für eine nachfolgende, dauerhaft gesicherte Übertragung dar.