web-dev-qa-db-de.com

Python: Binding Socket: "Adresse wird bereits verwendet"

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 die Verbindung beendet wird

comSocket.shutdown(1)
comSocket.close()

Mit Wireshark sehe ich den Socket mit FIN, ACK und ACK von beiden Seiten geschlossen. 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 das nächste 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 Vielen Dank!

60
Tu Hoang

Versuchen Sie es mit der Option SO_REUSEADDR socket, 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 nicht funktioniert. Wenn Sie versuchen, ein Socket zu binden und erneut eine Verbindung zu demselben Ziel herzustellen (mit aktiviertem SO_REUSEADDR), ist TIME_WAIT weiterhin in Kraft. Sie können jedoch eine Verbindung zu einem anderen Host: Port herstellen.

Ein paar Lösungen kommen mir in den Sinn. Sie können entweder weiter versuchen, bis Sie wieder eine Verbindung herstellen können. Oder wenn der Client das Schließen des Sockets (nicht des Servers) initiiert, sollte es magisch funktionieren.

97
Bryan

Hier ist der vollständige Code, den ich getestet habe, und er gibt mir KEINEN Fehler "Adresse bereits verwendet". Sie können dies in einer Datei speichern und die Datei im Basisverzeichnis der HTML-Dateien ausführen, die Sie bereitstellen möchten. Darüber hinaus können Sie Verzeichnisse vor dem Start 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
23
user2543899

Tatsächlich kann das SO_REUSEADDR-Flag zu viel größeren Konsequenzen führen: SO_REUSADDR erlaubt es Ihnen, einen in TIME_WAIT festgehaltenen Port zu verwenden, aber Sie können diesen Port immer noch nicht verwenden, um eine Verbindung zu dem zuletzt verbundenen Ort herzustellen. Was? Angenommen, ich wähle den lokalen Port 1010 aus und verbinde mich mit dem Port 300 von foobar.com. Schließen Sie ihn dann lokal und belassen Sie diesen Port in TIME_WAIT. Ich kann den lokalen Port 1010 sofort wiederverwenden, um eine Verbindung zu einer beliebigen Stelle herzustellen, mit Ausnahme von foobar.com Port 300.

Sie können den TIME_WAIT-Status jedoch vollständig vermeiden, indem Sie sicherstellen, dass das entfernte Ende die Schließung auslöst (close-Ereignis). So kann der Server Probleme vermeiden, indem der Client zuerst geschlossen wird. Das Anwendungsprotokoll muss so ausgelegt sein, dass der Client weiß, wann geschlossen werden soll. Der Server kann sicher als Antwort auf ein EOF vom Client geschlossen werden, er muss jedoch auch ein Timeout festlegen, wenn ein EOF erwartet wird, falls der Client das Netzwerk ungeschickt verlassen hat. In vielen Fällen reicht es aus, einige Sekunden zu warten, bis der Server geschlossen wird. 

Ich empfehle Ihnen auch, mehr über Netzwerk und Netzwerkprogrammierung zu erfahren. Du solltest jetzt zumindest wissen, wie das TCP-Protokoll funktioniert. Das Protokoll ist ziemlich trivial und klein und kann Ihnen daher in Zukunft viel Zeit sparen.

Mit dem Befehl netstat können Sie leicht erkennen, welche Programme ((Programmname, PID) Tuple) 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 für Linux-Netzwerkkonfigurationen finden Sie unter https://serverfault.com/questions/212093/how-to-reduce-number-of-sockets-in-time-wait

11
Rustem K

Sie müssen die allow_reuse_address vor dem Binden festlegen. Führen Sie anstelle des SimpleHTTPServer 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()

Dadurch wird verhindert, dass der Server bindet, bevor wir die Flags setzen konnten.

6
Vukasin Toroman

Falls Sie das Problem mit TCPServer oder SimpleHTTPServer haben, überschreiben Sie SocketServer.TCPServer.allow_reuse_address (Python 2.7.x) oder socketserver.TCPServer.allow_reuse_address (Python 3.x) Attribut

class MyServer(SocketServer.TCPServer):
    allow_reuse_address = True

server = MyServer((Host, PORT), MyHandler)
server.serve_forever()
6
Andrei

Wie von Felipe Cruze erwähnt, müssen Sie den SO_REUSEADDR vor dem Binden festlegen. Ich habe eine Lösung auf einer anderen Site gefunden - Lösung auf einer anderen Site, unten wiedergegeben

Das Problem ist, dass die Socket-Option SO_REUSEADDR vor .__ festgelegt werden muss. Die Adresse ist an den Sockel gebunden. Dies kann durch die Unterklasse .__ erfolgen. ThreadingTCPServer und Überschreiben der server_bind-Methode wie folgt:

 import SocketServer, socket 

 Klasse MyThreadingTCPServer (SocketServer.ThreadingTCPServer): 
 def server_bind (self): 
 self.socket.setsockopt (socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
 self.socket.bind (self.server_address) 

3

socket.socket() sollte vor socket.bind() laufen und wie gesagt REUSEADDR verwenden

2
Felipe Cruz

Für mich war die bessere Lösung folgende. Da die Initiative zum Schließen der Verbindung vom Server ausgeführt wurde, hatte setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) keine Auswirkungen und TIME_WAIT verhinderte eine neue Verbindung am selben Port mit Fehler:

[Errno 10048]: Address already in use. Only one usage of each socket address (protocol/IP address/port) is normally permitted 

Ich habe schließlich die Lösung verwendet, um das Betriebssystem den Port selbst auswählen zu lassen. Dann wird ein anderer Port verwendet, wenn der Präzedenzfall noch in TIME_WAIT ist.

Ich ersetzte:

self._socket.bind((guest, port))

mit:

self._socket.bind((guest, 0))

Wie in der Python-Socket-Dokumentation einer TCP-Adresse angegeben:

Wenn angegeben, muss source_address ein 2-Tuple (Host, Port) sein, an den der Socket als Quelladresse gebunden werden kann, bevor eine Verbindung hergestellt wird. Wenn Host oder Port "0" oder 0 ist, wird das Standardverhalten des Betriebssystems verwendet.

2
user2455528

eine andere Lösung, natürlich in der Entwicklungsumgebung, ist beispielsweise der Prozessabbruch

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
1
test30

Ich weiß, dass Sie eine Antwort bereits akzeptiert haben, aber ich glaube, dass das Problem damit zusammenhängt, bind () auf einem Client-Socket aufzurufen. Das mag in Ordnung sein, aber bind () und shutdown () scheinen nicht gut zusammen zu spielen. Auch SO_REUSEADDR wird im Allgemeinen mit Listen-Sockets verwendet. auf der Serverseite.

Du solltest IP und Port zum Verbinden () übergeben. So was:

comSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
comSocket.connect(('', 5555))

Rufen Sie nicht bind () auf, setzen Sie nicht SO_REUSEADDR.

1
jcoffland

Ich habe einen anderen Grund für diese Ausnahme gefunden ... Wenn ich die Anwendung von Spyder IDE aus ausführte (in meinem Fall war es Spyder3 auf Raspbian) und das Programm durch ^ C oder eine Ausnahme beendet wurde, 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 "Adresse bereits verwendet" gefunden. Die IDE scheint den neuen 'Lauf' als separaten Prozess zu starten, der den Socket findet, der vom vorherigen 'Lauf' verwendet wurde. 

socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

hat nicht geholfen.

Tötungsprozess 13210 hat geholfen. Starten des Python-Skripts von der Befehlszeile aus

python3 <app-name>.py

hat immer gut funktioniert, wenn SO_REUSEADDR auf true gesetzt wurde. Das neue Thonny IDE oder Idle3 IDE hatte dieses Problem nicht.

1
peets

Ich denke, der beste Weg ist, den Prozess an diesem Port zu beenden, indem Sie das Terminal fuser -k [PORT NUMBER]/tcp eingeben, z. fuser -k 5001/tcp.

0
Kiblawi_Rabee