111 votes

Python: Liaison de socket: "Adresse déjà utilisée"

J'ai une question concernant le socket client sur un 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()

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

[ERROR] Address already in use

Je me demande comment je peux libérer le port immédiatement pour pouvoir l'utiliser la prochaine fois.

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

setsockopt ne semble pas pouvoir résoudre le problème Merci!

153voto

Bryan Points 2341

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

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

Éditer : Je vois que vous avez toujours des problèmes avec ceci. 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é), alors TIME_WAIT sera toujours en vigueur. Cependant, cela vous permettra de vous connecter à un hôte:port différent.

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

37voto

user2543899 Points 81

Voici le code complet que j'ai testé et qui ne me donne absolument PAS d'erreur "adresse déjà utilisée". Vous pouvez enregistrer ceci dans un fichier et exécuter le fichier depuis le répertoire de base des fichiers HTML que vous souhaitez servir. De plus, vous pourriez changer de répertoire de manière programmatique avant de démarrer le serveur

import socket
import SimpleHTTPServer
import SocketServer
# import os # décommentez si vous voulez changer de répertoires dans le programme

PORT = 8000

# Absolument essentiel ! Cela assure que la réutilisation du socket est configurée AVANT
# qu'elle ne soit liée. Évitera le problème TIME_WAIT

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("/Mes/PagesWeb/EnDirect/ici.html")

httpd.serve_forever()

# httpd.shutdown() # Si vous voulez arrêter le serveur de manière programmatique

24voto

Rustem K Points 148

According to ce lien

En fait, le drapeau SO_REUSEADDR peut entraîner des conséquences beaucoup plus graves: SO_REUSADDR vous permet d'utiliser un port qui est bloqué en TIME_WAIT, mais vous ne pouvez toujours pas utiliser ce port pour établir une connexion avec le dernier endroit auquel il s'est connecté. Quoi? Supposons que je choisisse le port local 1010, et que je me connecte au port 300 de foobar.com, puis je ferme localement, laissant ce port en TIME_WAIT. Je peux réutiliser le port local 1010 immédiatement pour me connecter n'importe où sauf sur le port 300 de foobar.com.

Cependant, vous pouvez totalement éviter l'état TIME_WAIT en veillant à ce que l'extrémité distante initie la fermeture (événement de fermeture). Ainsi, le serveur peut éviter les problèmes en laissant le client se fermer en premier. Le protocole d'application doit être conçu de telle sorte que le client sache quand se fermer. Le serveur peut sûrement se fermer en réponse à un EOF du client, cependant, il devra également définir un délai d'attente lorsqu'il attend un EOF au cas où le client aurait quitté le réseau de manière incorrecte. Dans de nombreux cas, simplement attendre quelques secondes avant que le serveur ne se ferme sera suffisant.

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

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

Une très bonne explication des configurations réseau linux peut être trouvée ici.

16voto

Andrei Points 3689

Si vous rencontrez des problèmes avec TCPServer ou SimpleHTTPServer, remplacez SocketServer.TCPServer.allow_reuse_address (python 2.7.x) ou 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()

8voto

Vukasin Toroman Points 357

Vous devez définir l'allow_reuse_address avant de lier. Au lieu d'exécuter SimpleHTTPServer, exécutez ce snippet:

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 eu la chance de définir les drapeaux.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X