- TCP (Transmission Control Protocol) ist das zentrale Protokoll des Internets, das selbst in instabilen Netzwerkumgebungen eine zuverlässige und geordnete Datenübertragung ermöglicht
- Während IP nur für die Datenübermittlung zwischen Hosts zuständig ist, übernimmt TCP die prozessbasierte Kommunikation über Ports sowie Fehlerbehebung, Neuübertragung und Reihenfolgenkontrolle
- Durch Flow Control und Congestion Control wird gesteuert, dass weder die Grenzen des Empfangspuffers noch die verfügbare Netzwerkbandbreite überschritten werden
- Anhand von in C implementierten einfachen TCP-Server- und HTTP-Server-Beispielen werden das Erzeugen von Sockets, Binden, Lauschen, das Akzeptieren von Verbindungen sowie Sende- und Empfangsabläufe konkret erklärt
- Die interne Struktur von TCP mit Sequenz- und ACK-Nummern, Window, Checksum und Flags (SYN/ACK/FIN/RST) ist eine zentrale Grundlage für den stabilen Betrieb des heutigen Internets
Notwendigkeit und Rolle von TCP
- IP ist nur für die Übermittlung von Paketen zwischen Hosts zuständig; für die Kommunikation zwischen Prozessen ist eine Transportschicht wie TCP/UDP erforderlich
- Die IP-Adresse wird mit einem „Gebäude“ verglichen, der Port mit einer „Wohnung“; jede Anwendung kommuniziert, indem sie an einen Port gebunden wird
- TCP verbirgt Netzwerkinstabilitäten wie Paketverlust, Duplikate und vertauschte Reihenfolge und gewährleistet durch Neuübertragung und Checksum Zuverlässigkeit
- Router bleiben einfach, und die Zuverlässigkeit wird an den beiden Endpunkten der Kommunikation verarbeitet, wodurch die Komplexität der Netzwerkinfrastruktur reduziert wird
- Dank dieser Struktur funktionieren wichtige Internetdienste wie HTTP, SMTP und SSH stabil
Flow Control und Congestion Control
- Die Empfangsseite speichert Daten vorübergehend über den Receive Buffer des Kernels; dessen Größe wird mit
net.ipv4.tcp_rmem festgelegt
- Die Sendeseite erhält über das Feld Window die vom Empfänger zulässige Datenmenge und passt die Übertragungsmenge entsprechend an
- Um Congestion zu verhindern, die durch Unterschiede in der verfügbaren Bandbreite im gesamten Netzwerk entsteht, verwendet TCP Algorithmen zur Congestion Control
- Ausgelöst durch das Ereignis des Congestion Collapse von 1986 wurde ein Back-off-Mechanismus hinzugefügt
Beispiele für TCP-Server und HTTP-Server
- Ein in C geschriebener einfacher TCP-Echo-Server nimmt Eingaben des Clients entgegen und sendet sie mit vorangestelltem „you sent:“ zurück
- Verwendet werden die Berkeley-Sockets-API-Aufrufe
socket(), bind(), listen(), accept(), send() und recv()
- Während sich der Server in
sleep() befindet, warten die Daten des Clients im Receive Buffer und werden anschließend der Reihe nach verarbeitet
- In einem einfachen HTTP/1.1-Server-Beispiel werden Anfragen über eine TCP-Verbindung angenommen und ein
HTTP/1.1 200 OK-Header sowie ein Body gesendet
- Die Anzahl der Anfragen wird mit
i gezählt; bei einer Anfrage mit curl localhost:8080 wird eine Antwort in der Form „[1] Yo, I am a legit web server“ ausgegeben
Struktur von TCP-Segmenten und zentrale Felder
- Ein TCP-Segment besteht aus Quell- und Zielport, Sequenznummer, ACK-Nummer, Window-Größe, Checksum und Flags
- Ports sind jeweils 16 Bit breit, sodass bis zu 64K Ports verwendet werden können
- Eine Verbindung wird durch ein 5-Tupel identifiziert:
(Protokoll, Quell-IP, Quellport, Ziel-IP, Zielport)
- Die Sequenznummer gibt den Bereich der übertragenen Bytes an, die ACK-Nummer die bereits vollständig empfangenen Bytes
- Wenn Daten fehlen, bleibt das ACK an dieser Stelle stehen und wird nach der Neuübertragung als kumulatives ACK aktualisiert
- Flag-Bits steuern den Verbindungszustand
SYN/ACK stellen über den 3-Way-Handshake eine Verbindung her
FIN beendet die Verbindung über einen 4-Way-Handshake
RST trennt die Verbindung bei abnormalem Ende oder Fehlern sofort
- Das Window-Feld zeigt die empfangbare Datenmenge an; mit dem Befehl
ss lässt sich der Pufferstatus (rb131072, tb16384) prüfen
- Die Checksum dient der Fehlererkennung durch Summierung in 16-Bit-Einheiten innerhalb des Segments
Fazit
- TCP garantiert Zuverlässigkeit, Reihenfolge und Integrität und unterstützt Anwendungen dabei, auch in instabilen Internetumgebungen korrekt zu funktionieren
- Während vor Jahrzehnten schon die Übertragung einiger KB schwierig war, ist heute selbst 4K-Streaming alltäglich geworden
- Die Präzision von Design und Implementierung von TCP, die diese stabile Kommunikation ermöglicht, bildet die Grundlage für das kontinuierliche Wachstum des Internets
1 Kommentare
Hacker-News-Kommentare
Wenn man versucht, auf einer unzuverlässigen Datagramm-Schicht einen zuverlässigen Datenstrom aufzubauen, erhält man am Ende etwas, das TCP fast entspricht
Die anfänglichen Grenzen von TCP waren eine kleine Fenstergröße, unzureichende Behandlung verlorener Pakete und die Beschränkung auf nur einen einzelnen Stream
Um diese Probleme zu lösen, entstanden SCTP und QUIC
Algorithmen zur Staukontrolle sind kein Teil des Protokolls, sondern Code, der auf beiden Seiten jeder Verbindung läuft
Frühe Algorithmen (Reno, Vegas usw.) waren einfach, aber ausreichend wirksam, und danach wurde weiter zu großen Puffern, langen RTTs, Fairness usw. geforscht
Früher habe ich in JavaScript eine Bibliothek geschrieben, mit der sich mehrere Downloads innerhalb eines Streams mit Priorisierung und Abbruchfunktionen steuern ließen
Mit einem GreaseMonkey-Skript ließ ich die Thumbnails einer Dating-Seite im Hintergrund vorab laden, abhängig von der Scroll-Position
Dadurch wurde letztlich die Serverlast gesenkt und zugleich die Benutzererfahrung verbessert
Lustigerweise habe ich dieses Skript mit einem Match geteilt, und mit dieser Person bin ich bis heute zusammen — es war gewissermaßen Tinder vor Tinder
TCP ist eine Struktur, die über einem paketvermittelten Netz eine virtuelle Leitung bereitstellt; die Idee, Zuverlässigkeit durch Wiederübertragung umzusetzen, stammt aus dem französischen Netzwerk Cylades
Ein Angreifer kann überall im Netzwerk Daten injizieren oder mit einem RST-Paket die Verbindung trennen
Wenn man RST per Firewall blockiert, steigt zwar die Stabilität, aber Desynchronisierungsangriffe durch gefälschte Sequenznummern bleiben weiterhin möglich
Daher muss jede Anwendung über eine separate Verbindung eine Resume-Funktion implementieren und trägt zugleich das Problem von TCP Slow Start mit sich
Außerdem halte ich schon das Konzept, Adressen und Ports getrennt zu behandeln, für ineffizient
Bei DNS over TLS (DoT) kann man zum Beispiel über eine TCP-Verbindung mehrere Abfragen gleichzeitig senden und Antworten unabhängig von der Reihenfolge empfangen
Das ist effizienter und rücksichtsvoller, als mehrere Verbindungen zu öffnen
Ob QUIC schneller ist, weiß ich nicht, aber die Serverunterstützung ist noch begrenzt
HTTP/1.1-Pipelining macht etwas Ähnliches, aber die Antworten kommen der Reihe nach
Viele Universitätsvorlesungen betonen diesen Punkt jedoch nicht, sodass oft der Irrtum entsteht, TCP habe nur einen einzigen Algorithmus
Ich würde gern fragen, ob hier jemand eine besondere Zuneigung zu SCTP hat
SCTP ist ein Protokoll, das die Nachrichtenorientierung von UDP mit der Zuverlässigkeit von TCP verbindet und Multi-Streaming sowie Multihoming unterstützt
Es kann mehrere unabhängige Streams parallel übertragen, sodass sich der Text und die Bilder einer Webseite gleichzeitig senden lassen
Details dazu unter Wikipedia: Stream Control Transmission Protocol
Am Ende ist die beste Antwort eine Zuverlässigkeitsschicht über UDP, also QUIC
Ich habe mich gefragt, ob man nur mit IP direkt Pakete senden kann
Ich dachte, zwischengeschaltete Router würden Pakete verwerfen, die weder TCP noch UDP sind
Bei IPv4 genügt es, aus der Liste der IANA-Protokollnummern einen Wert zwischen 0 und 255 festzulegen
Core-Router prüfen dieses Feld nicht, NAT- oder ISP-Geräte können es aber prüfen
Zwischen zwei Linux-Servern ist sogar mit experimentellen Nummern (253, 254) Kommunikation möglich
Protokolle wie IPsec, GRE und L2TP sind ebenfalls weder TCP noch UDP
In Unternehmensnetzen mit Firewalls oder NAT können beliebige Protokolle blockiert werden
NAT hat das Ende-zu-Ende-Prinzip gebrochen, und am Ende begannen die Leute, alles auf TCP oder UDP oder sogar auf HTTP zu legen
Es hat allenfalls Auswirkungen darauf, dass die Entropie des ECMP-Hashs sinkt
Entscheidend ist letztlich, ob die Gegenstelle dieses Protokoll versteht
Portnummern sind lediglich Kennungen für Dienste innerhalb eines Knotens
RUDP (Plan9) war ein hervorragender Kompromiss zwischen TCP und UDP
Siehe Reliable User Datagram Protocol
Dadurch, dass TCP zum Standard wurde, wurde es selbst dann zwangsläufig verwendet, wenn weder Zuverlässigkeit noch Reihenfolgegarantie nötig waren
Jetzt, da sich HTTP/3 (auf QUIC-Basis) verbreitet, könnte sich die Lage verbessern
QUIC ist allerdings deutlich komplexer, und seine Stärke ist nur für einen Teil der Nutzer wirklich nützlich
Eine einfache Verschlüsselungsschicht im Stil von UDP + WireGuard könnte die bessere Alternative sein
TCP ist eine der großen Erfindungen der Menschheit, hat aber die Herrschaft des halbverbindungsorientierten Netzwerks (NAT-basiert) nicht vorhergesehen
hätten die Ingenieure damals wohl gefragt, warum man es absichtlich so kompliziert machen sollte
Letztlich stammen die heutige asymmetrische Link-Struktur und die Trennung zwischen Client und Server aus genau dieser Denkweise
Der Algorithmus zur Staukontrolle von TCP hat interessante Effekte, die vielen Entwicklern nicht bekannt sind
Wenn man auf einer neuen Verbindung Daten sendet, ist die anfängliche Übertragung langsam, und der Geschwindigkeitsanstieg wird durch die Latenz bestimmt
In Rechenzentren kann schon eine Verringerung der RTT um einige Mikrosekunden zu deutlich höherer Geschwindigkeit führen
Die meisten TCP-Stacks berechnen den Geschwindigkeitsanstieg nicht pro Byte, sondern pro Segment, sodass man mit Jumbo Frames sechsmal schneller ansteigen kann
AWS investiert aus diesem Grund viel in geringe Switching-Latenz und Unterstützung für Jumbo Frames
Experten nehmen solche Feinabstimmungen vor, aber die meisten wundern sich nur, warum über einen 10-Gbps-Link keine 10 Gbps erreicht werden
Ein eigenes Protokoll über IP zu bauen war sehr einfach
Noch vor 15 Jahren konnte man in Python experimentieren, indem man Pakete direkt zusammensetzte