Wie kann man einen Socket in TIME_WAIT zwangsweise schließen?

Ich lasse ein bestimmtes Programm unter Linux laufen, das manchmal abstürzt. Wenn man es danach schnell öffnet, hört es auf Socket 49201 statt auf 49200 wie beim ersten Mal. netstat zeigt, dass 49200 im Zustand TIME_WAIT ist.

Gibt es ein Programm, das Sie ausführen können, um den Socket sofort aus dem TIME_WAIT-Zustand zu holen?

Lösung
/etc/init.d/networking restart

Lassen Sie mich das näher erläutern. Das Transmission Control Protocol (TCP) ist als bidirektionales, geordnetes und zuverlässiges Datenübertragungsprotokoll zwischen zwei Endpunkten (Programmen) konzipiert. In diesem Zusammenhang bedeutet der Begriff "zuverlässig", dass es die Pakete erneut überträgt, wenn sie in der Mitte verloren gehen. TCP garantiert die Zuverlässigkeit, indem es für ein einzelnes oder eine Reihe von Paketen, die es von der Gegenstelle erhalten hat, Bestätigungspakete (ACK) zurücksendet.

Dies gilt auch für die Kontrollsignale wie Abbruchanforderung/Antwort. In RFC 793 wird der TIME-WAIT-Zustand wie folgt definiert:

TIME-WAIT - steht für das Warten auf

genügend Zeit vergeht, um sicher zu sein das entfernte TCP hat die Bestätigung seiner Verbindungsanfrage erhalten Beendigungsanforderung.

Siehe das folgende TCP-Zustandsdiagramm:

TCP ist ein bidirektionales Kommunikationsprotokoll, d. h. wenn die Verbindung aufgebaut ist, gibt es keinen Unterschied zwischen Client und Server. Außerdem kann jeder von beiden kündigen, und beide Partner müssen sich auf das Schließen einigen, um eine bestehende TCP-Verbindung vollständig zu beenden.

Nennen wir den ersten, der quittiert, den aktiven Closer und den anderen Peer den passiven Closer. Wenn der aktive Closer FIN sendet, geht der Status auf FIN-WAIT-1. Dann empfängt er ein ACK für das gesendete FIN und der Status wechselt zu FIN-WAIT-2. Sobald er auch vom passiven Verteiler ein FIN erhält, sendet der aktive Verteiler das ACK zum FIN und der Zustand geht auf TIME-WAIT über. Falls der passive Verteiler das ACK für das zweite FIN nicht erhalten hat, sendet er das FIN-Paket erneut.

In RFC 793 wird die TIME-OUT-Zeit auf das Doppelte der maximalen Segmentlebensdauer (Maximum Segment Lifetime, 2MSL) festgelegt. Da MSL, die maximale Zeit, die ein Paket im Internet unterwegs sein kann, auf 2 Minuten festgelegt ist, beträgt 2MSL 4 Minuten. Da es keine ACK auf eine ACK gibt, kann der aktive Verschließer nichts anderes tun, als 4 Minuten zu warten, wenn er sich korrekt an das TCP/IP-Protokoll hält, nur für den Fall, daß der passive Absender die ACK auf sein FIN (theoretisch) nicht erhalten hat.

In der Realität sind fehlende Pakete wahrscheinlich selten, und sehr selten, wenn alles innerhalb des LAN oder innerhalb eines einzelnen Rechners passiert.

Um die Frage wortwörtlich zu beantworten, wie man einen Socket in TIME_WAIT zwangsweise schließt, bleibe ich bei meiner ursprünglichen Antwort:

/etc/init.d/networking restart

Praktisch gesprochen würde ich es so programmieren, dass es den TIME-WAIT-Zustand ignoriert, indem ich die Option SO_REUSEADDR verwende, wie WMR erwähnte. Was genau macht SO_REUSEADDR?

Diese Socket-Option teilt dem Kernel mit Diese Socket-Option teilt dem Kernel mit, dass, selbst wenn dieser Port beschäftigt ist (im

dem TIME_WAIT-Zustand), trotzdem weiter ihn trotzdem wiederverwenden. Wenn er besetzt ist, aber mit einem anderen Status, erhalten Sie trotzdem eine Adresse bereits in Gebrauch Fehler. Es ist nützlich, wenn Ihr Server heruntergefahren worden ist heruntergefahren und dann sofort wieder hochgefahren wurde während die Sockets noch auf seinem Port aktiv sind. Sie sollten sich bewusst sein, dass wenn unerwartete Daten eintreffen, können sie Ihren Server verwirren können, aber obwohl dies ist zwar möglich, aber nicht wahrscheinlich.

Kommentare (5)

Ich weiß nicht, ob Sie den Quellcode des Programms haben, das Sie ausführen, aber wenn ja, könnten Sie SO_REUSEADDR über setsockopt(2) setzen, was Ihnen erlaubt, sich an dieselbe lokale Adresse zu binden, selbst wenn der Socket im TIME_WAIT-Zustand ist (es sei denn, der Socket hört aktiv zu, siehe socket(7)).

Weitere Informationen über den TIME_WAIT-Zustand finden Sie in der Unix socket FAQ.

Kommentare (5)

Soweit ich weiß, gibt es keine Möglichkeit, den Socket zwangsweise zu schließen, außer man schreibt einen besseren Signalhandler in sein Programm, aber es gibt eine /proc-Datei, die steuert, wie lange der Timeout dauert. Die Datei lautet

/proc/sys/net/ipv4/tcp_tw_recycle

und Sie können die Zeitüberschreitung auf 1 Sekunde setzen, indem Sie dies tun:

echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle 

Allerdings enthält diese Seite eine Warnung über mögliche Zuverlässigkeitsprobleme beim Setzen dieser Variable.

Es gibt auch eine verwandte Datei

/proc/sys/net/ipv4/tcp_tw_reuse

die steuert, ob TIME_WAIT-Sockets wiederverwendet werden können (vermutlich ohne Timeout).

Übrigens warnt die Kernel-Dokumentation davor, einen dieser Werte ohne 'Ratschläge/Anfragen von technischen Experten' zu ändern. Was ich nicht bin.

Das Programm muss so geschrieben worden sein, dass es versucht, sich an den Port 49200 zu binden und dann um 1 erhöht wird, wenn der Port bereits in Gebrauch ist. Wenn Sie die Kontrolle über den Quellcode haben, könnten Sie dieses Verhalten dahingehend ändern, dass Sie ein paar Sekunden warten und es dann am gleichen Port erneut versuchen, anstatt zu inkrementieren.

Kommentare (3)