web-dev-qa-db-fra.com

Python: Binding Socket: "Adresse déjà utilisée"

J'ai une question concernant le socket client sur le réseau TCP/IP. Disons que j'utilise

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)

Le socket créé sera lié au port 5555. Le problème est qu'après la fin de la connexion

comSocket.shutdown(1)
comSocket.close()

Avec Wireshark, je vois que le socket est fermé avec FIN, ACK et ACK des deux côtés, je ne peux plus utiliser le port. Je reçois l'erreur suivante:

[ERROR] Address already in use

Je me demande comment puis-je vider le port tout de suite pour pouvoir utiliser le même port la prochaine fois.

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

setsockopt ne semble pas être capable de résoudre le problème. Merci.

60
Tu Hoang

Essayez d’utiliser l’option de socket SO_REUSEADDR avant de lier le socket.

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

Edit: Je vois que vous avez toujours des problèmes avec ça. Il y a un cas où SO_REUSEADDR ne fonctionnera pas. Si vous essayez de lier un socket et de vous reconnecter à la même destination (avec SO_REUSEADDR activé), TIME_WAIT sera toujours en vigueur. Il vous permettra toutefois de vous connecter à un autre port: hôte.

Quelques solutions me viennent à l’esprit. Vous pouvez continuer de réessayer jusqu'à ce que vous puissiez rétablir la connexion. Ou si le client initie la fermeture du socket (pas le serveur), alors cela devrait fonctionner comme par magie.

97
Bryan

Voici le code complet que j'ai testé et ne me donne absolument PAS l'erreur "adresse déjà utilisée". Vous pouvez l'enregistrer dans un fichier et l'exécuter à partir du répertoire de base des fichiers HTML que vous souhaitez diffuser. En outre, vous pouvez modifier par programme les répertoires avant de démarrer le serveur.

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

En réalité, l'indicateur SO_REUSEADDR peut avoir des conséquences bien plus graves: SO_REUSADDR vous permet d'utiliser un port bloqué dans TIME_WAIT, mais vous ne pouvez toujours pas utiliser ce port pour établir une connexion avec le dernier lieu de connexion. Quoi? Supposons que je sélectionne le port local 1010 et que je me connecte au port 300 de foobar.com, puis que je ferme localement, laissant ce port dans TIME_WAIT. Je peux immédiatement réutiliser le port local 1010 pour me connecter n’importe où sauf le port 300 de foobar.com.

Cependant, vous pouvez complètement éviter l'état TIME_WAIT en vous assurant que l'extrémité distante initie la fermeture (événement de fermeture). Ainsi, le serveur peut éviter les problèmes en laissant le client fermer en premier. Le protocole d'application doit être conçu pour que le client sache quand fermer. Le serveur peut fermer en toute sécurité en réponse à un EOF du client, mais il devra également définir un délai d'attente lorsqu'il attend un EOF dans le cas où le client aurait quitté le réseau de manière disgracieuse. Dans de nombreux cas, il suffira d'attendre quelques secondes avant la fermeture du serveur. 

Je vous conseille également d'en apprendre davantage sur la mise en réseau et la programmation en réseau. Vous devriez maintenant au moins comment le protocole TCP fonctionne. Le protocole est relativement simple et petit et peut donc vous faire gagner beaucoup de temps à l’avenir.

Avec la commande netstat, vous pouvez facilement voir quels programmes ((nom_programme, pid) Tuple) sont liés à quels ports et quel est l'état actuel du socket: TIME_WAIT, CLOSING, FIN_WAIT, etc.

Vous trouverez une très bonne explication des configurations réseau Linux/ https://serverfault.com/questions/212093/how-to-reduce-number-of-sockets-in-time-wait

11
Rustem K

Vous devez définir allow_reuse_address avant de lier. À la place du SimpleHTTPServer, exécutez cet extrait:

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()

Cela empêche le serveur de se lier avant que nous ayons pu définir les drapeaux.

6
Vukasin Toroman

Si vous rencontrez des difficultés avec TCPServer ou SimpleHTTPServer, Override SocketServer.TCPServer.allow_reuse_address (python 2.7.x). ou socketserver.TCPServer.allow_reuse_address (attribut python 3.x)

class MyServer(SocketServer.TCPServer):
    allow_reuse_address = True

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

Comme Felipe Cruze l’a mentionné, vous devez définir SO_REUSEADDR avant la liaison. J'ai trouvé une solution sur un autre site - solution sur un autre site, reproduit ci-dessous

Le problème est que l'option de socket SO_REUSEADDR doit être définie avant l'adresse est liée à la socket. Cela peut être fait en sous-classant ThreadingTCPServer et en remplaçant la méthode server_bind comme suit:

 import SocketServer, socket 

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

3
Maurice Flanagan

socket.socket() devrait s'exécuter avant socket.bind() et utiliser REUSEADDR comme indiqué

2
Felipe Cruz

Pour moi, la meilleure solution était la suivante. Puisque l'initiative de fermer la connexion a été faite par le serveur, la setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) n'a eu aucun effet et TIME_WAIT évitait une nouvelle connexion sur le même port avec l'erreur suivante:

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

J'ai finalement utilisé la solution pour laisser le système d'exploitation choisir le port lui-même. Un autre port est utilisé si le précédent est toujours dans TIME_WAIT.

J'ai remplacé:

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

avec:

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

Comme indiqué dans la documentation relative au socket python d’une adresse tcp:

Si elle est fournie, adresse_source doit être une adresse 2-Tuple (hôte, port) à laquelle le socket doit se lier en tant qu'adresse source avant la connexion. Si hôte ou port sont ‘’ ou 0 respectivement, le comportement par défaut du système d’exploitation sera utilisé.

2
user2455528

une autre solution, dans l'environnement de développement bien sûr, consiste à tuer le processus en l'utilisant, par exemple

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

Je sais que vous avez déjà accepté une réponse, mais je pense que le problème concerne l’appel de bind () sur un socket client. Cela peut être correct, mais bind () et shutdown () ne semblent pas bien jouer ensemble. En outre, SO_REUSEADDR est généralement utilisé avec les sockets d’écoute. c'est-à-dire côté serveur.

Vous devriez passer et ip/port pour vous connecter (). Comme ça:

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

N'appelez pas bind (), ne définissez pas SO_REUSEADDR.

1
jcoffland

J'ai trouvé une autre raison pour cette exception . Lors de l'exécution de l'application à partir de Spyder IDE (dans mon cas, il s'agissait de Spyder3 sur Raspbian) et que le programme s'est terminé par ^ C ou une exception, le socket était toujours actif:

Sudo netstat -ap | grep 31416
tcp  0  0 0.0.0.0:31416  0.0.0.0:*    LISTEN      13210/python3

L'exécution du programme à nouveau a trouvé l'adresse "déjà utilisée"; le IDE semble démarrer la nouvelle "exécution" en tant que processus séparé qui recherche le socket utilisé par la précédente "exécution". 

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

n'a pas aidé.

Le processus de tuer 13210 a aidé. Démarrer le script python à partir de la ligne de commande, comme

python3 <app-name>.py

a toujours bien fonctionné lorsque SO_REUSEADDR était défini sur true. Le nouveau Thonny IDE ou Idle3 IDE n'a pas eu ce problème.

1
peets

Je pense que le meilleur moyen est simplement de tuer le processus sur ce port, en tapant le terminal fuser -k [PORT NUMBER]/tcp, par exemple. fuser -k 5001/tcp.

0
Kiblawi_Rabee