orm@doc-tcpip.org

Erstellt: Oktober 1999 - Letzte Modifikation: Juni 2001

[ Main | Local ]


Die TCP-"State Machine"

Die States CLOSE_WAIT, FIN_WAIT, LISTEN etc.

Allgemeines zur Implementation von TCP

Auf Unix-Systemen werden TCP-Verbindungen über Sockets realisiert. Ein Socket ist eine Datenstruktur, die über einen Port, eine IP-Adresse und ein Protokoll definiert wird. Die Informationen zu dieser Struktur finden sich im Protocol Control Block (PCB) und im TCP Control Block (TCB). Für eine Verbindung, die als Transportprotokoll TCP benutzt, wird der Zustand und die Parameter der Verbindung im TCB gespeichert. Zwischen den Zustanden (States) gibt es genau festgelegte Übergänge - die TCP State Machine.

Der jeweilige Status einer Verbindung oder Socket ist entweder mit dem netstat-Kommando (netstat -a) oder mit einem Networkdebugger (unter AIX der ndb) zu sehen.

ESTABLISHED

Die Verbindung ist ordnungsgemäß aufgebaut. Es können Daten fließen. Das ist der Normalzustand einer bestehenden Verbindung.

LISTEN

Ein Prozess hat lokal einen Socket ohne Verbindungspartner geöffnet. Der Prozess "lauscht" auf diesem Socket auf Anforderungen für den Angebotenen Dienst.

TIME_WAIT

Dieser Socket ist vor kurzer Zeit geschloßen worden. Ein geschloßener Socket wird in der Regel nicht sofort wieder belegt, da ja noch verirrte Pakete auf dem Netz sein könnten. Würde man den Socket sofort wieder öffnen, dann müßten solche Pakete als Pakete für diese Verbindung betrachtet werden und ausgwertet werden. Das würde zu unsinnigen Ergebnissen führen - deshalb bleibt ein Socket solange in TIME_WAIT, wie nach technischem Ermessen ein Paket maximal auf dem Netz bleiben kann. Standard ist dafür 2 * MSL (Maximum Segment Livetime). Also zweimal die Zeit, die ein Paket braucht, um an das äußeerste Ende des Netzes und zurück zu laufen. Heute werden dafür Werte aus den ermittelten RTTs (Round Trip Time, Rundlauf-Zeiten) berechnet.

CLOSE_WAIT

Dieser TCB hat ein FIN vom fernen TCB erhalten und hat dieses FIN mit einem ACK beantwortet. TCP als Protokoll wartet jetzt darauf, das die Application den Socket schliesst oder weiter Daten sendet. Es muß jedenfalls einen Prozess geben, dem dieser Socket gehört - stoppt man diesen Prozess, dann wird der Socket gelöscht. Sollte es keinen Prozess geben, dann kann man von einem Bug in der TCP-IP Implementation ausgehen.

Es wäre denkbar, das eine Verbindung extrem kurzlebig ist, also die Abfolge von SYN, SYN-ACK, ACK, Daten, FIN, und ACK so schnell abläuft, das die Server-Applikation noch gar keine Zeit hatte, die Verbindunge zu übernehmen. Trotzdem ist das ein Bug oder zumindest ein unverantwortlich großes Backlog auf der Serverseite.

FIN_WAIT_1

In diesen Status geht der Socket, wenn er von der Applikation signalisiert bekommen hat, das er die Verbindung von seiner Seite schließen soll. Es ist also der Moment, indem ein FIN-Paket gesendet wurde, aber noch kein ACK oder FIN ACK empfangen wurde. Die andere Seite hat also noch nicht zu erkennen gegeben, daß sie die Absicht, die Verbindung zu beenden, begriffen hat.

FIN_WAIT_2

In diesem Status wartet der Socket auf ein FIN von der anderen Seite der Verbindung. Er hat seinerseits ein FIN geschickt und auch ein ACK empfangen - die andere Seite hat also anerkannt, das die Verbindung geschloßen werden soll. Per Default tut ein TCP-Socket das für 2 Stunden. Dann schickt er ein Keepalive Packet. Das ist ein Paket mit einer schon gesendeten Sequenznummer, er erwartet also von der anderen Seite ein erneutes ACK, was er als Bestätigung nimmt, das die Verbindung noch steht. Der Zähler wird dann zurück gesetzt und der Zyklus beginnt von neuem. Das wird oft als "Hängen" der Verbindung gesehen.

Gibt es keine Antwort auf das Keepalive Paket, so wird erneut angefragt. Im Normalfall werden 8 Anfragen geschickt, dann sendet der TCB ein RST und ändert den Status nach CLOSED. Das TCP geht jetzt davon aus, das der Verbindungspartner nicht mehr existiert (die Maschine ist gecrashed oder hat den Socket einseitig abgebaut).

Unter AIX läßt sich das Verhalten mit dem no-Kommando beeinflußen:

SYN_SENT

Kein Status, den man normalerweise im netstat sieht. Das lokale TCP hat auf Anforderung einer Applikation ein SYN gesendet, um eine Verbindung aufzubauen.

SYN_RCVD

Kein Status, den man normalerweise im netstat sieht. Das lokale TCP hat ein SYN empfangen, aber noch nicht darauf reagiert.

Hier die Timer zu den normalen Übergängen

Als "normalen" Übergang für einen "normalen" Socket sehe ich diese Transformation an: ESTABLISHED ==> FIN_WAIT1 ==> FIN_WAIT2 ==> TIME_WAIT ==> Closing Das Schließen eines Socket ist im praktischen Betrieb interessant, weil hier die meisten Probleme auftreten. Hier das Ganze in den Teilschritten:

ESTABLISHED ==> FIN_WAIT1

Die Verbindung steht und Daten sind geflossen, es ist an der Applikation, zu entscheiden, wann Schluß sein soll und mittels des close()-Systemcalls das dem TCP mitzuteilen - also kein Timer hier.

FIN_WAIT1 ==> FIN_WAIT2

Die Zeit zwischen den beiden FIN_WAIT Staten ist ganz vom Partner der Verbindung abhängig. Unser FIN-Paket kann auf dem Netz verloren gegangen sein, der Partner kann langsam oder unter großer Last sein, oder sein ACK-Paket schafft es nicht zu uns... Hier ist also eindeutig ein Timer nötig. Dazu dient der TCP Retransmit Timer, der aus den gemittelten Rundlauf-Zeiten die nötigen Werte dynamisch ermittelt. Auf einer AIX Maschine gibt es eine Reihe no-Parameter, die es dem User gestatten, die Werte zu beinflußen: rto_length, rto_limit, rto_low, rto_high . Dazu mehr auf einer anderen Seite und natürlich in der Man-Page zum no-Kommando.

FIN_WAIT2 ==> TIME_WAIT

Hier gibt es nur dann einen Timeout, wenn der Socket mit der "SO_KEEPALIVE" Option gestartet wurde. In diesem Fall sendet der Socket Keepalive-Proben und der Socket wird geschlossen, wenn die Gegenseite nicht antworten sollte. Ist diese Option nicht gesetzt, so befindet sich der Socket in eine Zustand, den man als "half-closed" bezeichnet. Der Socket hängt dann für immer im FIN_WAIT_2. Die Timer sind bei der Status-Beschreibung erwähnt.

TIME_WAIT ==> CLOSED

Diese Totzeit ist nötig, um eventuell verzögerte Pakete für diesen Socket wirkungsvoll vom Netz zu ziehen. Unter AIX kann man den dazu gehörigen Timer beeinflußen. Dazu dient der no-Parameter "tcp_timewait". Der angegebene Wert ist die Anzahl von 15 Sekunden Intervallen, die gewartet werden soll. Danach läuft ein Job, der Verbindungen in diesem Zustand schließt. Es sind also 15 Sekunden das einstellbare Minimum. Dieser Wert ist heute dynamisch ermittelt. Früher war es ein statischer Wert: 2*MSL, Maximum Segment Lifetime. Also die maximale Zeit, die ein Paket auf dem Netz unter allen Umständen leben kann. Das hängt natürlich direkt von der physikalischen Ausdehnung des Netzes ab; früher wurden in der Regel etwa 2 Minuten eingestellt.


[ Main | Local ]

[ Allgemein | UNIX | AIX | TCP-IP | TCP | ROUTING | DNS | NTP | NFS | FreeBSD | Linux | SMTP | Tracing | GPS ]

Copyright 2001-2014 by Orm Hager - Es gilt die GPL
Feedback bitte an: Orm Hager (orm@doc-tcpip.org )