Ich habe eine Frage zum Client-Socket im TCP / IP-Netzwerk. Nehmen wir an, ich benutze
try:
comSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
comSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
except socket.error, msg:
sys.stderr.write("[ERROR] %s\n" % msg[1])
sys.exit(1)
try:
comSocket.bind(('', 5555))
comSocket.connect()
except socket.error, msg:
sys.stderr.write("[ERROR] %s\n" % msg[1])
sys.exit(2)
Der erstellte Socket wird an Port 5555 gebunden. Das Problem besteht darin, dass nach dem Beenden der Verbindung
comSocket.shutdown(1)
comSocket.close()
Bei Verwendung von Wireshark sehe ich, dass die Buchse von beiden Seiten mit FIN, ACK und ACK geschlossen ist. Ich kann den Port nicht mehr verwenden. Ich erhalte folgende Fehlermeldung:
[ERROR] Address already in use
Ich frage mich, wie ich den Port sofort löschen kann, damit ich beim nächsten Mal immer noch denselben Port verwenden kann.
comSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
setsockopt scheint das Problem nicht lösen zu können. Danke!
TIME_WAIT
Socket-Status verursacht wird , für den die folgende Antwort eine Lösung bietet :)Antworten:
Versuchen Sie es mit der
SO_REUSEADDR
Socket-Option, bevor Sie den Socket binden.comSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
Edit: Ich sehe, du hast immer noch Probleme damit. Es gibt einen Fall, in dem
SO_REUSEADDR
es nicht funktioniert. Wenn Sie versuchen, einen Socket zu binden und erneut eine Verbindung zum selben Ziel herzustellen (mitSO_REUSEADDR
aktiviert),TIME_WAIT
bleibt dies weiterhin wirksam. Sie können jedoch eine Verbindung zu einem anderen Host herstellen: Port.Ein paar Lösungen kommen in den Sinn. Sie können entweder so lange wiederholen, bis Sie wieder eine Verbindung herstellen können. Oder wenn der Client das Schließen des Sockets initiiert (nicht des Servers), sollte dies auf magische Weise funktionieren.
quelle
setsockopt
vorbind
? Wurde der erste Socket mitSO_REUSEADDR
oder nur der ausgefallene erstellt? Die wartende Steckdose mussSO_REUSEADDR
eingestellt sein, damit dies funktioniertbind()
, aber SO_REUSEADDR gilt für alle Sockets, einschließlich nicht nur TCP-Server-Sockets, sondern auch TCP-Client-Sockets und UDP-Sockets. Veröffentlichen Sie hier keine Fehlinformationen.Hier ist der vollständige Code, den ich getestet habe und der mir KEINEN Fehler "Adresse wird bereits verwendet" gibt. Sie können dies in einer Datei speichern und die Datei aus dem Basisverzeichnis der HTML-Dateien ausführen, die Sie bereitstellen möchten. Darüber hinaus können Sie Verzeichnisse vor dem Starten des Servers programmgesteuert ändern
import socket import SimpleHTTPServer import SocketServer # import os # uncomment if you want to change directories within the program PORT = 8000 # Absolutely essential! This ensures that socket resuse is setup BEFORE # it is bound. Will avoid the TIME_WAIT issue class MyTCPServer(SocketServer.TCPServer): def server_bind(self): self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.socket.bind(self.server_address) Handler = SimpleHTTPServer.SimpleHTTPRequestHandler httpd = MyTCPServer(("", PORT), Handler) # os.chdir("/My/Webpages/Live/here.html") httpd.serve_forever() # httpd.shutdown() # If you want to programmatically shut off the server
quelle
Wenn Sie das Problem mit
TCPServer
oder habenSimpleHTTPServer
, überschreiben Sie das AttributSocketServer.TCPServer.allow_reuse_address
(Python 2.7.x) odersocketserver.TCPServer.allow_reuse_address
(Python 3.x)class MyServer(SocketServer.TCPServer): allow_reuse_address = True server = MyServer((HOST, PORT), MyHandler) server.serve_forever()
quelle
Nach diesem Link
Sie können den Status TIME_WAIT jedoch vollständig vermeiden, indem Sie sicherstellen, dass das Remote-Ende das Schließen initiiert (Ereignis close). So kann der Server Probleme vermeiden, indem er den Client zuerst schließen lässt. Das Anwendungsprotokoll muss so konzipiert sein, dass der Client weiß, wann er schließen muss. Der Server kann als Antwort auf eine EOF vom Client sicher geschlossen werden. Er muss jedoch auch eine Zeitüberschreitung festlegen, wenn er eine EOF erwartet, falls der Client das Netzwerk unangemessen verlassen hat. In vielen Fällen reicht es aus, nur ein paar Sekunden zu warten, bevor der Server geschlossen wird.
Ich rate Ihnen auch, mehr über Networking und Netzwerkprogrammierung zu erfahren. Sie sollten jetzt zumindest wissen, wie das TCP-Protokoll funktioniert. Das Protokoll ist recht trivial und klein und kann Ihnen daher in Zukunft viel Zeit sparen.
Mit dem
netstat
Befehl können Sie leicht sehen, welche Programme ((Programmname, PID) Tupel) an welche Ports gebunden sind und wie der aktuelle Socket-Status ist: TIME_WAIT, CLOSING, FIN_WAIT und so weiter.Eine wirklich gute Erklärung der Linux-Netzwerkkonfigurationen finden Sie unter /server/212093/how-to-reduce-number-of-sockets-in-time-wait .
quelle
Sie müssen die allow_reuse_address vor dem Binden festlegen. Führen Sie anstelle des SimpleHTTPServers dieses Snippet aus:
Handler = SimpleHTTPServer.SimpleHTTPRequestHandler httpd = SocketServer.TCPServer(("", PORT), Handler, bind_and_activate=False) httpd.allow_reuse_address = True httpd.server_bind() httpd.server_activate() httpd.serve_forever()
Dies verhindert, dass der Server bindet, bevor wir die Flags setzen können.
quelle
Wie Felipe Cruze erwähnt hat, müssen Sie vor dem Binden SO_REUSEADDR festlegen. Ich habe eine Lösung auf einer anderen Site gefunden - eine Lösung auf einer anderen Site, die unten wiedergegeben ist
quelle
Ich habe einen anderen Grund für diese Ausnahme gefunden. Beim Ausführen der Anwendung von Spyder IDE (in meinem Fall Spyder3 auf Raspbian) und Beenden des Programms durch ^ C oder eine Ausnahme war der Socket noch aktiv:
sudo netstat -ap | grep 31416 tcp 0 0 0.0.0.0:31416 0.0.0.0:* LISTEN 13210/python3
Beim erneuten Ausführen des Programms wurde die "bereits verwendete Adresse" gefunden. Die IDE scheint den neuen 'Lauf' als separaten Prozess zu starten, der den vom vorherigen 'Lauf' verwendeten Socket findet.
socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
hat nicht geholfen.
Tötungsprozess 13210 half. Starten des Python-Skripts über die Befehlszeile wie
funktionierte immer gut, wenn SO_REUSEADDR auf true gesetzt war. Die neue Thonny IDE oder Idle3 IDE hatte dieses Problem nicht.
quelle
socket.socket()
sollte vorher laufensocket.bind()
undREUSEADDR
wie gesagt verwendenquelle
Für mich war die bessere Lösung die folgende. Da die Initiative zum Schließen der Verbindung vom Server durchgeführt wurde,
setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
hatte dies keine Auswirkung und TIME_WAIT vermied eine neue Verbindung auf demselben Port mit dem Fehler:[Errno 10048]: Address already in use. Only one usage of each socket address (protocol/IP address/port) is normally permitted
Ich habe die Lösung schließlich verwendet, um das Betriebssystem den Port selbst auswählen zu lassen. Dann wird ein anderer Port verwendet, wenn sich der Präzedenzfall noch in TIME_WAIT befindet.
Ich ersetzte:
mit:
self._socket.bind((guest, 0))
Wie in der Python-Socket-Dokumentation einer TCP-Adresse angegeben:
quelle
Eine andere Lösung, natürlich in der Entwicklungsumgebung, ist beispielsweise das Beenden von Prozessen
def serve(): server = HTTPServer(('', PORT_NUMBER), BaseHTTPRequestHandler) print 'Started httpserver on port ' , PORT_NUMBER server.serve_forever() try: serve() except Exception, e: print "probably port is used. killing processes using given port %d, %s"%(PORT_NUMBER,e) os.system("xterm -e 'sudo fuser -kuv %d/tcp'" % PORT_NUMBER) serve() raise e
quelle
Ich weiß, dass Sie bereits eine Antwort akzeptiert haben, aber ich glaube, das Problem hat mit dem Aufruf von bind () auf einem Client-Socket zu tun. Dies mag in Ordnung sein, aber bind () und shutdown () scheinen nicht gut zusammen zu spielen. Außerdem wird SO_REUSEADDR im Allgemeinen mit Listen-Sockets verwendet. dh auf der Serverseite.
Sie sollten übergeben und IP / Port zu verbinden (). So was:
comSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) comSocket.connect(('', 5555))
Rufen Sie bind () nicht auf, setzen Sie SO_REUSEADDR nicht.
quelle
bind()
undshutdown()
haben genau nichts miteinander zu tun, und der Vorschlag, dass "sie nicht gut zusammen zu spielen scheinen", ist unbegründet. Sie haben den Teil verpasst, in dem er den lokalen Hafen 5555 nutzen muss.Ich denke, der beste Weg ist, den Prozess an diesem Port zu beenden, indem Sie das Terminal eingeben
fuser -k [PORT NUMBER]/tcp
, zfuser -k 5001/tcp
.quelle