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?
113
3
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
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:
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
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, siehesocket(7)
).Weitere Informationen über den TIME_WAIT-Zustand finden Sie in der Unix socket FAQ.
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
und Sie können die Zeitüberschreitung auf 1 Sekunde setzen, indem Sie dies tun:
Allerdings enthält diese Seite eine Warnung über mögliche Zuverlässigkeitsprobleme beim Setzen dieser Variable.
Es gibt auch eine verwandte Datei
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.