Beispiele für Dienste von Transportprotokollen
Je nach Transportschichtprotokoll kann das Dienstmodell z. B. umfassen Bytestromtransport oder Übertragung isolierter Nachrichten/Pakete
zuverlässigen oder unzuverlässigen Datentransport
Reihenfolgeerhaltung
zugesicherte feste oder minimal verfügbare Datenraten
Garantien zu Latenzzeiten und ihren Schwankungen (Jitter)
Zeitdauer bis zum abgeschlossenen Verbindungsaufbau
Schutz gegen Mithören, Verändern, Wiederholen, Unterdrücken,. . . von Daten Priorisierung von Daten/Verbindungen
maximal entstehende Kosten für die Verbindung
Wie kann eine Anwendung mit dem Transportschichtprotokoll sprechen
Application Programming Interface, API
am weitesten verbreitete API für die Protokolle der TCP/IP-Familie ist die Socket-API
Sie ermöglicht es Anwendungen z. B., TCP-Verbindungen auf- oder abzubauen und Daten darüber zu verschicken und zu empfangen
Sehr flexibel
Transportschicht multiplexing
Portnummern
Als Portnummern werden sowohl in TCP als auch in UDP 16-Bit-Zahlen verwendet, also der Bereich 0–65535
Manche Portnummern werden üblicherweise für bestimmte Anwendungen verwendet: „Well-known Ports“
z. B. TCP Port 80 für HTTP, TCP Port 25 für SMTP, UDP+TCP Port 53 für DNS Liste wird von der IANA verwaltet
Eine Sonderrolle haben außerdem die sog. „privilegierten Ports“ mit den Nummern 0–1023 die meisten Betriebssysteme erlauben nur Anwendungen mit Administratorrechten, diese Portnummern lokal zu verwenden
Wie entscheidet das Netzwerkschichtprotokoll an welche von mehreren Transportschichten ein eingehendes Paket weitergereicht werden soll?
Achtung: TCP Port x ist etwas völlig anderes als UDP Port x – beide existieren unabhängig voneinander und können auch unabhängig voneinander genutzt werden
In der TCP/IP-Welt wird hierfür die Protokollnummer verwendet
Ein Eintrag im IP-Header gibt an, welches Transportschichtprotokoll verwendet wird,
z. B. 6 = TCP
17 = UDP
User Datagram Protocol (UDP)
Verbindungsloser Datagrammdienst, der auf IP aufsetzt
UDP-Datagramme werden unabhängig voneinander unzuverlässig übertragen
UDP „erbt“ den Best-Effort-Dienst von IP und fügt außer Portnummern kaum etwas hinzu also insbes. keine Erkennung verlorener oder verdoppelter Pakete
UDP-Paketformat
Wie entscheidet ein UDP-Empfänger, an welchen Socket er ein eingehendes Datagramm zustellen soll?
Ein UDP-Socket wird eindeutig durch das Tupel aus Ziel-IP-Adresse und Ziel-Port identifiziert
Wofür UDP?
Der Einsatz von UDP kann sinnvoll sein, wenn es auf (Reaktions-)geschwindigkeit ankommt und mögliche Verluste akzeptabel sind
Da kein Verbindungsaufbau nötig ist, entsteht hierfür auch keine Verzögerung
Kommunikation mit mehreren Gegenstellen über einen einzigen Socket Vorteil, wenn mit vielen gleichzeitig kommuniziert wird!
Kleinen Header, daher nur wenige zusätzliche Bytes für Verwaltungsinformationen (kleiner Protokolloverhead)
UDP kann immer so schnell senden, wie es die Anwendung wünscht (muss natürlich nicht alles ankommen. . . )
UDP wird aus diesen Gründen oft für Multimedia-Protokolle eingesetzt (aber z. B. auch für DNS)
Transmission Control Protocol (TCP)
Das Dienstmodell von TCP umfasst die zuverlässige, reihenfolgeerhaltende Übertragung eines Bytestromes
Dafür wird der Bytestrom beim Sender in Pakete (bei TCP Segmente genannt) zerlegt und beim Empfänger wieder zusammengesetzt (eine solche Zerlegung nennt man allgemein Segmentierung, das Zusammenfügen Reassemblierung)
TCP ermöglicht Vollduplex-Kommunikation, es werden also Byteströme in beide Richtungen übertragen
TCP unterstützt nur Unicast-Kommunikation zwischen genau zwei Gegenstellen, also keine Übertragungen desselben Bytestroms an mehrere Empfänger
Darf TCP Segmente, die von unterschiedlichen Quellen stammen, an denselben Ziel-Socket zustellen?
Daten von unterschiedlichen Absender-Sockets beim Empfänger zu „mischen“ macht bei einem zuverlässigen Bytestrom-Dienst keinen Sinn – also Nein!
Wie entscheidet ein TCP-Empfänger, an welchen Socket ein eingehendes Segment gehen soll?
Anhand des 4-Tupels (Quell-IP-Adresse, Quell-Portnummer, Ziel-IP-Adresse, Ziel-Portnummer) Hier sind also Quell-Portnummer und Quell-IP-Adresse wichtig für die richtige Zuordnung
Eine Teilmenge des 4-Tupels ist im Allgemeinen nicht ausreichend – anders als beim verbindungslosen UDP!
Beispiel http:
Kann es mehrere gleichzeitig aktive TCP-Sockets mit derselben Portnummer für dieselbe (lokale) IP-Adresse geben?
Ja – das kommt sogar sehr häufig vor!
Wenn ein Server mit mehreren Clients spricht, dann sprechen alle mit demselben Port
. . . aber für jeden der Clients wird ein eigener Socket erzeugt
Was genau bedeutet wohl „zuverlässig“ im Zusammenhang mit TCP?
dass der Sender irgendwann eine Bestätigung bekommt, dass die Daten zugestellt wurden – oder zumindest eine Fehlermeldung, wenn eben das nicht sichergestellt werden konnte
Wie trotz Übertragungsfehler zuverlässigkeit realisieren?
Problem: Auch ACKs oder NACKs sind Pakete und können kaputtgehen und dadurch Pakete doppelt versendet werden! Wie kann der Empfänger das feststellen?
Wir können die Pakete durchnummerieren
An der Sequenznummer kann der Empfänger erkennen, dass er das Paket bereits hat, und kann das Duplikat verwerfen
Wenn wir das umsetzen, dann können wir uns die NACKs sparen und nur mit ACKs arbeiten – das vereinfacht das Protokoll
Dafür schreiben wir in das ACK-Paket jeweils die Sequenznummer des letzten korrekt empfangenen Paketes
Heute oft praktiziert:
Startsequenznummer zufällig wählen
ausreichend großen Sequenznummernbereich verwenden und das Beste hoffen
Stop-and-Wait ist nicht sinnvoll, da zu langsam! -> Pipelining
Go-back-n
Eins von zwei für die zuverlässige Datenübertragung mit Pipelining weit verbreiteten „Grundschemata“:
Sender: ACK für Paket x bedeutet, dass auch alle vorangegangenen Pakete erfolgreich angekommen sind (kumulatives ACK)
Fehler werden durch einen Timeout erkannt überträgt bei einem erkannten Fehler für Paket x alle Pakete ab x erneut
Empfänger: quittiert(sendet) bei jedem Empfang das letzte in der richtigen Reihenfolge empfangene Paket (ggf. also viele ACKs mit derselben Sequenznummer)
verwirft alle Pakete, die nicht in der richtigen Reihenfolge ankommen –
Variante/Erweiterung: Puffern von Paketen „außer der Reihe“ kann ggf. die Effizienz steigern
Selective Repeat
Sender: erkennt Fehler an Lücken in Quittierung wiederholt nur Übertragung nicht quittierter Pakete
Erkennung fehlender ACKs durch Sende-Timeouts
Empfänger: speichert alle empfangenen Pakete quittiert jedes Paket
Aufwändiger als Go-back-n – aber (je nach Umgebung, Einsatzzweck) evtl. auch effizienter
Zuverlässigkeit in TCP
Die TCP-Zuverlässigkeitsmechanismen orientieren sich (in erster Näherung) an Go-back-n
TCP nutzt 32-Bit-Sequenznummern
Für jede Bytestrom Richtung werden unabhängige Sequenznummern verwendet
ACKs in TCP
Im TCP-Header gibt es ein ACK-Feld genauer: ACK-Flag + ACK-Sequenznummernfeld
Piggybacked ACKs: Dies ist eine Technik, bei der ACKs (Bestätigungen) für empfangene Segmente nicht in separaten TCP-Segmenten gesendet werden, sondern zusammen mit den Daten in umgekehrter Richtung "huckepack" (piggyback) mitgeschickt werden.
Jedes TCP-Segment in eine Richtung kann gleichzeitig ein ACK in die andere Richtung sein (piggybacked ACKs)
Wenn in die Gegenrichtung gerade keine Daten zu verschicken sind, dann ist ein ACK ein leeres TCP-Segment (Nutzdatenlänge 0) mit gesetztem ACK-Feld
ACKs in TCP sind kumulativ, bestätigen also nicht nur das letzte empfangene Datenpaket sonder auch vorangegangene
Da ein (kumulatives!) ACK immer auch alle früheren Segmente bestätigt, können Segmente hinter einer „Lücke“ oder in falscher Reihenfolge eintreffende Segmente nicht bestätigt werden
TCP wiederholt dann – genau wie klassisches Go-Back-n – jeweils einmal das vorherige ACK
Die TCP-Spezifikation schreibt nicht vor, ob außer der Reihe eintreffende Segmente vom Empfänger zwischengespeichert werden
(sie dürfen auch verworfen werden – wegen Neuübertragungen werden sie noch einmal ankommen, heutige TCP-Implementationen puffern solche Daten aber praktisch immer zwischen)
Bytesequenznummern in TCP
TCP nummeriert nicht Segmente, sondern alle Bytes im übertragenen Bytestrom durch Zahl im Sequenznummern-Feld = Sequenznummer des ersten im Segment enthaltenen Bytes
hat ein Segment die Sequenznummer 3217 und enthält 500 Bytes Daten, dann hat das nächste Segment die Sequenznummer 3717
Zahl im ACK-Feld = Sequenznummer des ersten Bytes an, das dem Sender des ACK im Bytestrom noch fehlt, also nicht (!!!) die höchste bereits empfangene Sequenznummer:
Wie lange ist „zu lange“ bezüglich Timeouts und ACKs? Retransmission Timeout
Idee: Beobachte die typische RTT der Verbindung und verwende das als Grundlage für den Timeout
Wenn ein ACK ankommt, liefert uns das automatisch auch eine Messung der RTT (wir wissen ja, wann wir das Segment abgeschickt hatten)
Darüber können wir einen (exponentiell gleitenden) Mittelwert berechnen
Die RTT selbst ist aber keine gute Wahl den Timeout dann würde ja ein minimal verzögertes ACK schon eine Neuübertragung verursachen
Wie wählt man also basierend auf den RTT-Messungen den „richtigen“ Timeout-Wert?
Ursprünglich einmal in TCP: das Doppelte der gemessenen durchschnittlichen RTT auch nicht gut
Also: Standardabweichung (und damit „Breite“ der Verteilung) sollte mit einfließen
TCP misst deshalb außerdem, wie weit die RTTs typischerweise vom Mittelwert abweichen (erneut gleitender Mittelwert)
Der Timeout wird in TCP dann wie folgt gewählt:
RTT + 4 · Standardabweichung
Exponentielle Timeouts bei TCP
Verzögerte ACKs /Delayed ACKs
Heutige TCP-Implementationen verringern die Zahl generierter ACK-Nachrichten durch einen Mechanismus namens Delayed ACKs (DACK); wenn ein TCP-Empfänger. . .
. . . ein Segment in richtiger Reihenfolge mit der erwarteten Sequenznummer erhält und alle vorangegangenen Daten schon bestätigt wurden
⇒ das ACK wird verzögert und auf das nächste Segment gewartet; erst wenn nach (max.) 500 ms immer noch kein weiteres Segment ankommt, wird das ACK verschickt
. . . ein Segment in richtiger Reihenfolge mit der erwarteten Sequenznummer ankommt und ein vorangegangenes ACK verzögert wurde
⇒ sofort ein gemeinsames (kumulatives!) ACK für beide Segmente verschicken
. . . ein Segment mit einer höheren Sequenznummer als erwartet empfängt
⇒ eine Lücke!
⇒ sofort das vorangegangene ACK wiederholen (mit der – noch immer – erwarteten nächsten Sequenznummer = Anfang der Lücke)
. . . ein Segment erhält, das eine vorhandene Lücke (teilweise) schließt
⇒ sofort ein ACK mit dem nächsten noch fehlenden Byte verschicken
Was passiert, wenn ein einzelnes Segment verloren geht, nachfolgende aber ankommen?
Fast Retransmit
Der Sender wird mehrere ACKs mit derselben Sequenznummer bekommen Das lässt sich ausnutzen:
wenn ein Sender drei Duplikate eines ACKs erhalten hat (Triple Duplicate ACK, TDACK), nimmt er an, dass das Segment verloren gegangen ist
dann wird ein Fast Retransmit durchgeführt: das Segment wird schon vor dem Timeout wiederholt
Neu übertragen wird nur das eine Segment
Aufbau des TCP-Headers
TCP-Flags
Insgesamt 9 Bits im TCP-Header sind sogenannte Flags
Die wichtigsten davon: ACK: Segment bestätigt korrekt empfangene Daten, der Inhalt des ACK-Nummern-Feldes ist gültig
RST: der Sender des Segments will die Verbindung abbrechen (Fehlerfall) SYN: wird beim Verbindungsaufbau verwendet FIN: Verbindungsabbau, es werden keine weiteren Daten in dieser Senderichtung folgen
Außerdem: URG-Flag zum Anzeigen besonders wichtiger Daten (heute irrelevant) und drei Flags für spezielle Erweiterungen der Überlastkontrollmechanismen
TCP-Optionen
Die Optionen 0, 1 und 2 müssen von jeder TCP-Implementation unterstützt werden
0 bedeutet: „Ende der Optionsliste“ der Rest ist Padding
1 bedeutet: „dieses Byte der Optionsliste wird nicht genutzt“ kann „Lücken“ zwischen Optionen füllen
2 (Maximum Segment Size, MSS) wird verwendet, um im ersten Segment einer neuen Verbindung mitzuteilen, wie groß die maximale Segmentgröße ist, die die TCP-Implementation verarbeiten kann
die MSS muss laut Standard mindestens 536 Byte sein viele Implementationen können aber mehr wenn die Option nicht verwendet wird, nimmt TCP eine MSS von 536 Byte an (schlecht!!)
Die Timestamp-Option ist eine sehr einfache und „typische“ TCP-Option
Ziel: Mit jedem empfangenen ACK einen Zeitstempel transportieren, zu welchem Zeitpunkt das zugehörige Paket verschickt wurde
⇒ einfacherMechanismusfürsehrgenaueRTT-Messungen!
Das Verfahren wird angewandt, wenn der Client die Option im ersten Paket beim Verbindungsaufbau verwendet und der Server das in seinem ersten Antwortpaket ebenfalls tut
das bedeutet: beide Seiten unterstützen die Option
Der Timestamp-Optionseintrag ist 10 Bytes lang 1 Byte für den Optionstyp (Wert: 8)
1 Byte für die Länge des Optionseintrags (immer 10) 4 Byte für einen aktuellen Zeitstempel seiner Uhr in Senderichtung 4 Byte spiegelt den empfangenen Zeitstempel in der Rückrichtung
Dienstprimitive
TCP-Verbindungsaufbau
Zu Beginn einer Verbindung werden die TCP-Startsequenznummern in jede Richtung beliebig (normalerweise: zufällig) gewählt.
Zum Aufbauen einer Verbindung verwendet TCP einen sogenannten Drei-Wege-Handshake:
Zunächst schickt der Client (also der Endpunkt, der die Verbindung aufbaut) ein Segment mit gesetztem SYN-Flag an den Server; die von ihm gewählte Anfangssequenznummer steht im Sequenznummernfeld (SYN-Segment)
Der Server antwortet mit einem SYN-ACK-Segment: er setzt das SYN- und das ACK-Flag, bestätigt im ACK-Nummern-Feld die initiale Sequenznummer des Clients +1 (!) und gibt im Sequenznummernfeld seine eigene Startsequenznummer an
Schließlich bestätigt der Client das SYN-ACK mit einem ACK, in dem er die initiale Sequenznummer des Servers – wieder +1 – bestätigt
Wieso ist ein Drei-Wege-Handshake notwendig? Warum genügt nicht auch ein SYN/SYNACK-Handshake?
Wenn nur ein Zwei-Wege-Handshake erfolgen müsste, dann müsste der Server die eingehenden Daten akzeptieren und an die Anwendung weitergeben Das dritte Handshake-Paket (genauer: die korrekt wiedergegebene Startsequenznummer des Servers) bestätigt dem Server also, dass der Client „lebt“ und jetzt die Verbindung aufbauen möchte
Außerdem bietet es zusätzlichen Schutz vor einem Angreifer, der auf diese Weise keine TCP-Verbindung mit gefälschter Quelladresse vortäuschen kann, ohne die Antworten zu hören
TCP-Verbindungsabbau
Die beiden Richtungen der TCP-Verbindung können unabhängig voneinander geschlossen werden
Auch nachdem die Richtung A → B geschlossen wurde, können noch Daten von B zu A fließen das nennt man „halbgeschlossene Verbindung“ (nicht verwechseln mit einer „halboffenen Verbindung“ vor Abschluss des 3-Wege-Handshakes!)
Wenn auch die Gegenseite die Verbindung gleich schließt, ist es möglich, dass das ACK für das erste FIN und das zweite FIN zu einem Segment zusammengefasst werden
diese Nachrichten werden aber durch unterschiedliche Ereignisse (Empfang des FIN bzw. lokaler Aufruf des CLOSE-Dienstprimitivs) ausgelöst und deshalb zu unterschiedlichen Zeitpunkten erzeugt
Wann kann ein Host die Informationen zu einer geschlossenen TCP-Verbindung „vergessen“?
Informationen über eine geschlossene Verbindung müssen so lange „gemerkt“ werden, bis der Host sicher sein kann, dass die Gegenstelle das ACK auf ihr FIN erhalten hat
sonst könnte ja eine Übertragungswiederholung des FIN eintreffen, auf die korrekt mit einem wiederholten ACK reagiert werden muss. . .
Das lässt sich in einem Netz mit dem Dienstmodell des Internet nicht ohne weiteres garantieren – wer zuerst sein FIN geschickt hatte, hat deshalb ein Problem!
In der Praxis merken sich die Implementationen die Informationen für eine gewisse Zeitspanne (z. B. 4 Minuten)
Das ist ein echtes Problem für TCP-Implementationen! ein Webserver mit 10 000 Verbindungen pro Sekunde muss bei 4 Minuten Wartezeit ständig Informationen über 2 Millionen alte TCP-Verbindungen vorhalten!
Eine komplette TCP Sitzung
Reset(RST)
Neben dem „geordneten“ Schließen mit FIN-Segmenten kann eine TCP-Verbindung auch abgebrochen werden
Der Empfang eines Segmentes mit gesetztem RST-Flag (Reset) bedeutet, dass ab sofort keine weiteren Segmente gesendet werden dürfen und alle zukünftig eintreffenden Segmente verworfen werden sollen
Zweck: Fehlerbehandlung, zum Beispiel nach dem Absturz von einem der Kommunikationspartner
RST wird deshalb vor allem dann gesendet, wenn ein Segment für eine unbekannte TCP-Verbindung eintrifft
Nach einem Reset gibt es keine Garantien, ob gesendete Daten zugestellt wurden oder nicht!
Ein RST darf niemals als Antwort auf ein eingehendes RST gesendet werden (um sog. „Reset Wars“ zu vermeiden)
Nagle-Algorithmus
Ein TCP-Sender kann den Nagle-Algorithmus (Nagle’s Algorithm) verwenden, um das Tinygram-Problem zu vermeiden
Grundidee:
solange ein unbestätigtes Segment unterwegs ist
. . . und noch nicht genug Daten für ein volles Segment (also weniger als eine MSS) zum Senden vorliegen
. . . dann verzögere das Absenden des nächsten Segments
Tinygram-Problem: wenn die Anwendung immer gleich nach dem Absenden eines solchen „kleinen“ Segments wieder ein paar wenige Daten schreibt
Welchen Teil des Sequenznummernraumes muss der Sender in seinem Sendepuffer zwischenspeichern?
Wenn die Anwendung weiter Daten erzeugt, ist der zur Verfügung stehende Platz für den Sendepuffer irgendwann voll:
Der Sender muss die Daten erzeugende höhere Schicht dann in irgendeiner Form „bremsen“
typisches Vorgehen bei TCP-Sockets: sendende Anwendung blockiert beim Versuch, auf einen Socket mit vollem Sendepuffer zu schreiben
Welcher Sequenznummernbereich muss empfängerseitig gepuffert werden?
Wie kann der Sender wissen, wie viel beim Empfänger maximal noch zwischengespeichert werden kann?
Empfänger muss entsprechende Rückmeldung liefern
-> wird typischerweise gemeinsam mit ACKs übertragen
Empfänger meldet zusammen mit jedem ACK die verbleibende Datenmenge, die hinter den gerade bestätigten Daten noch in den Puffer passt („Sendekredit“)
diese kreditbasierte Flusskontrolle trifft man in der Praxis am häufigsten an – auch bei TCP das Receive-Window-Feld (RWIN) im TCP-Header trägt genau diese Information (gemessen wiederum in Byte)
Welchen Sendekredit wird der Empfänger dem Sender mit seinem letzten ACK zurückmelden? Welches Problem ergibt sich?
Es wird eine Fenstergröße von 0 zurückgemeldet Der Sender dürfte damit nie wieder Daten senden! . . . er würde deshalb auch nie wieder ein ACK bekommen . . . und so nie erfahren, dass wieder Platz im Empfangspuffer frei geworden ist
Lösung in TCP:
der Sender darf auch bei Empfangsfenster 0 ab und zu ein Segment („Probe Packet“, 1 Byte groß) senden
immer, wenn der Persist-Timer (= leichte Abwandlung des Retransmission Timers, der bei rwin = 0 aktiv wird) abläuft, wird ein neuer Versuch gestartet
der Empfänger sendet dann jeweils ein ACK, darin steht die aktuelle rwin-Wert
der Empfänger darf (muss nicht!) auch von sich aus erneut ein Fenster-Update (= wiederholtes ACK mit höherer Fenstergröße) schicken, sobald wieder Platz ist
sicherlich sinnvoll (und wird in der Praxis gemacht)
das alleine würde aber nicht ausreichen, denn das Fenster-Update könnte ja verloren gehen
der Probe-Mechanismus ist deshalb in jedem Fall notwendig!
Was passiert, wenn der Empfänger den Puffer erst volllaufen lässt, und die höhere Schicht die Daten dann immer wieder in sehr kleinen Einheiten vom Socket liest?
Das Fenster geht immer wieder um wenige Bytes auf Der Sender wird so gezwungen, viele sehr kleine Segmente zu verschicken Jeweils mit vollständigem TCP+IP+. . . -Header – das ist sehr ineffizient! Dieses Problem ist bekannt als Silly Window Syndrome Im Prinzip ist das ein Problem der Anwendung: schlecht implementiert! Aber schlecht implementierte Anwendungen gibt es häufig!
Lösung:
Empfänger wartet, bis sinnvoll viel Puffer (z. B. halbe Puffergröße oder eine MSS) verfügbar ist
Erst dann wird der Sender über das wieder geöffnete Fenster informiert
Auch der Nagle-Algorithmus auf der anderen Seite der Verbindung, falls aktiviert, kann das Problem lösen
Ab maximale fenstergröße hab ich nichts mehr gecheckt
Last changed6 months ago