Python: reliure 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] %sn" % msg[1])
    sys.exit(1)

try:
    comSocket.bind(('', 5555))

    comSocket.connect()

except socket.error, msg:

    sys.stderr.write("[ERROR] %sn" % msg[1])

    sys.exit(2)

la socket créée sera reliée au port 5555. Le problème est qu'après avoir terminé la connexion

comSocket.shutdown(1)
comSocket.close()

avec wireshark, je vois la prise fermée 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 je peux nettoyer le port tout de suite ainsi la prochaine fois, je pourrai utiliser le même port.

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

setsockopt ne semble pas être en mesure de résoudre le problème Je vous remercie!

58
demandé sur Tu Hoang 2011-06-17 04:12:11

12 réponses

essayez d'utiliser l'option SO_REUSEADDR socket avant de lier la socket.

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

Edit: Je vois que vous êtes toujours de la difficulté avec cela. Il y a un cas où SO_REUSEADDR ne marchera pas. Si vous essayez de lier une socket et de vous reconnecter à la même destination (avec SO_REUSEADDR activé), alors TIME_WAIT sera toujours en vigueur. Il vous permettra cependant de vous connecter à un port host:différent.

un couple de les solutions viennent à l'esprit. Vous pouvez soit continuer à retrier jusqu'à ce que vous pouvez gagner une connexion à nouveau. Ou si le client initie la fermeture de la socket (pas le serveur), alors il devrait fonctionner comme par magie.

91
répondu Bryan 2011-06-17 01:34:33

voici le code complet que j'ai testé et ne me donne absolument pas d'erreur "adresse déjà utilisée". Vous pouvez enregistrer cela dans un fichier et exécuter le fichier à partir du répertoire de base des fichiers HTML que vous voulez servir. De plus, vous pouvez modifier les répertoires par programmation 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
22
répondu user2543899 2013-09-17 19:57:25

en fait, le drapeau SO_REUSEADDR peut avoir des conséquences beaucoup plus importantes: SO_REUSADDR vous permet d'utiliser un port qui est bloqué dans TIME_WAIT, mais vous ne pouvez toujours pas utiliser ce port pour établir une connexion au dernier endroit où il s'est connecté. Quoi? Supposons que je choisisse le port local 1010, et que je me connecte à foobar.com port 300, puis fermer localement, laissant ce port dans TIME_WAIT. Je peux réutiliser le port local 1010 tout de suite pour me connecter à n'importe où sauf pour foobar.com port 300.

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 de façon à ce que le client sache quand fermer. Le serveur peut se 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 au cas où le client aurait quitté le réseau de manière ingrate. Dans de nombreux cas, le simple fait d'attendre un peu quelques secondes avant la fermeture du serveur seront suffisantes.

je vous conseille également d'en savoir plus sur le réseautage et la programmation réseau. Vous devriez maintenant au moins savoir comment fonctionne le protocole tcp. Le protocole est assez trivial et petit et donc, peut vous sauver beaucoup de temps à l'avenir.

avec la commande netstat vous pouvez facilement voir quels programmes ((program_name,pid) tuple) sont liés à quels ports et quel est l'état actuel de la socket: TIME_WAIT, CLOSING, FIN_WAIT et ainsi de suite.

une très bonne explication des configurations réseau linux peut être trouvée https://serverfault.com/questions/212093/how-to-reduce-number-of-sockets-in-time-wait .

11
répondu Rustem K 2017-04-13 12:13:38

vous devez définir allow_reuse_address avant de lier. Au lieu du simple serveur exécuter 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 une chance de mettre les drapeaux.

6
répondu Vukasin Toroman 2014-11-03 22:02:37

dans le cas où vous faites face au problème en utilisant TCPServer ou SimpleHTTPServer , outrepasser 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()
5
répondu Andrei 2016-08-02 20:06:01

socket.socket() doit précéder socket.bind() et utiliser REUSEADDR comme dit

2
répondu Felipe Cruz 2011-06-17 00:56:05

comme Felipe Cruze l'a mentionné, vous devez définir le SO_REUSEADDR avant de lier. J'ai trouvé une solution sur un autre site - solution sur un autre site, reproduit ci-dessous

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

import SocketServer, socket

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

2
répondu Maurice Flanagan 2012-09-28 13:03:56

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

[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 L'OS choisir le port lui-même, puis un autre port est utilisé si le précédent est encore dans TIME_WAIT.

j'ai remplacé:

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

avec:

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

comme il a été indiqué dans la documentation des sockets python d'une adresse tcp:

si fourni, source_address doit être un 2-tuple (hôte, port) pour que la socket se lie à comme son adresse source avant de se connecter. Si host ou port sont " ou 0 respectivement le comportement par défaut de L'OS sera utilisé.

2
répondu user2455528 2013-06-05 12:10:03

je sais que vous avez déjà accepté une réponse mais je crois que le problème est lié à l'appel de bind() sur une socket client. Cela pourrait être OK mais bind() et shutdown () ne semblent pas bien jouer ensemble. De plus, SO_REUSEADDR est généralement utilisé avec les sockets listen. c'est à dire du côté du serveur.

vous devriez passer et ip/port to connect(). Comme ceci:

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

Ne pas appeler bind(), ne définissez pas SO_REUSEADDR.

0
répondu jcoffland 2012-11-13 03:18:07

une autre solution, dans l'environnement de développement bien sûr, est de 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
0
répondu test30 2014-04-03 13:52:50

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

0
répondu Kiblawi_Rabee 2017-04-12 10:54:20

j'ai trouvé une autre raison pour cette exception. Lors de L'exécution de L'application à partir de Spyder IDE (dans mon cas C'était Spyder3 sur Raspbian) et le programme terminé par ^C ou une exception, la socket était encore active:

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

exécutant le programme a de nouveau trouvé l '"adresse déjà utilisée"; L'IDE semble lancer le nouveau' run 'comme un processus séparé qui trouve la socket utilisée par le'run' précédent.

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

n'a pas aidé.

processus de mise à mort 13210 aidé. Démarrer le script python à partir de la ligne de commande comme

python3 <app-name>.py

a toujours bien fonctionné quand SO_REUSEADDR a été mis à true. Le nouveau Thonny IDE ou Idle3 IDE n'avait pas ce problème.

0
répondu peets 2018-05-16 07:04:06