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.
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.
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
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 .
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.
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()
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)
socket.socket()
devrait s'exécuter avant socket.bind()
et utiliser REUSEADDR
comme indiqué
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é.
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
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.
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.
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
.