Tengo una pregunta sobre el socket del cliente en la red TCP / IP. Digamos que uso
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)
El socket creado estará vinculado al puerto 5555. El problema es que después de finalizar la conexión
comSocket.shutdown(1)
comSocket.close()
Al usar wirehark, veo el zócalo cerrado con FIN, ACK y ACK desde ambos lados, no puedo usar el puerto nuevamente. Obtuve el siguiente error:
[ERROR] Address already in use
Me pregunto cómo puedo limpiar el puerto de inmediato para que la próxima vez pueda usar ese mismo puerto.
comSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
setsockopt no parece poder resolver el problema ¡Gracias!
TIME_WAIT
estado del socket, para el cual la respuesta a continuación tiene una solución :)Respuestas:
Intente usar la
SO_REUSEADDR
opción de socket antes de atar el socket.comSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
Editar: veo que todavía tienes problemas con esto. Hay un caso en el
SO_REUSEADDR
que no funcionará. Si intenta vincular un socket y volver a conectarse al mismo destino (conSO_REUSEADDR
habilitado),TIME_WAIT
seguirá en vigor. Sin embargo, le permitirá conectarse a un host diferente: puerto.Me vienen a la mente un par de soluciones. Puede seguir intentándolo hasta que pueda volver a conectarse. O si el cliente inicia el cierre del socket (no el servidor), entonces debería funcionar mágicamente.
fuente
setsockopt
antesbind
? ¿Se creó el primer socketSO_REUSEADDR
o solo el fallado? el enchufe de espera debe estarSO_REUSEADDR
configurado para que esto funcionebind()
, pero SO_REUSEADDR es para todos los sockets, incluidos no solo los sockets de servidor TCP, sino también los sockets de cliente TCP y los sockets UDP. No publique información errónea aquí.Aquí está el código completo que he probado y absolutamente NO me da un error de "dirección ya en uso". Puede guardar esto en un archivo y ejecutar el archivo desde el directorio base de los archivos HTML que desea servir. Además, puede cambiar de directorio mediante programación antes de iniciar el servidor
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
fuente
En caso de que se enfrente al problema usando
TCPServer
oSimpleHTTPServer
, anule elSocketServer.TCPServer.allow_reuse_address
atributo (python 2.7.x) osocketserver.TCPServer.allow_reuse_address
(python 3.x)class MyServer(SocketServer.TCPServer): allow_reuse_address = True server = MyServer((HOST, PORT), MyHandler) server.serve_forever()
fuente
Según este enlace
Sin embargo, puede evitar completamente el estado TIME_WAIT asegurándose de que el extremo remoto inicie el cierre (evento de cierre). Entonces, el servidor puede evitar problemas dejando que el cliente se cierre primero. El protocolo de la aplicación debe diseñarse para que el cliente sepa cuándo cerrar. El servidor puede cerrarse de forma segura en respuesta a un EOF del cliente, sin embargo, también deberá establecer un tiempo de espera cuando espera un EOF en caso de que el cliente haya abandonado la red de manera desagradable. En muchos casos, basta con esperar unos segundos antes de que se cierre el servidor.
También te aconsejo que aprendas más sobre redes y programación de redes. Ahora debería al menos cómo funciona el protocolo tcp. El protocolo es bastante trivial y pequeño y, por lo tanto, puede ahorrarle mucho tiempo en el futuro.
Con el
netstat
comando puede ver fácilmente qué programas ((program_name, pid) tuple) están vinculados a qué puertos y cuál es el estado actual del socket: TIME_WAIT, CLOSING, FIN_WAIT, etc.Puede encontrar una muy buena explicación de las configuraciones de red de Linux /server/212093/how-to-reduce-number-of-sockets-in-time-wait .
fuente
Debe establecer allow_reuse_address antes de vincular. En lugar de SimpleHTTPServer, ejecute este fragmento:
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()
Esto evita que el servidor se vincule antes de que tengamos la oportunidad de configurar las banderas.
fuente
Como mencionó Felipe Cruze, debe configurar SO_REUSEADDR antes de vincular. Encontré una solución en otro sitio - solución en otro sitio, que se reproduce a continuación
fuente
Encontré otra razón para esta excepción. Al ejecutar la aplicación desde Spyder IDE (en mi caso fue Spyder3 en Raspbian) y el programa terminó con ^ C o una excepción, el socket seguía activo:
sudo netstat -ap | grep 31416 tcp 0 0 0.0.0.0:31416 0.0.0.0:* LISTEN 13210/python3
Al ejecutar el programa nuevamente, se encontró la "Dirección ya en uso"; el IDE parece iniciar la nueva 'ejecución' como un proceso separado que encuentra el socket utilizado por la 'ejecución' anterior.
socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
no ayudó.
El proceso de matanza 13210 ayudó. Iniciando el script de Python desde la línea de comandos como
siempre funcionó bien cuando SO_REUSEADDR se estableció en verdadero. El nuevo Thonny IDE o Idle3 IDE no tenía este problema.
fuente
socket.socket()
debe ejecutarse antessocket.bind()
y usarREUSEADDR
como se dijofuente
Para mí, la mejor solución fue la siguiente. Dado que la iniciativa de cerrar la conexión fue realizada por el servidor, el
setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
no tuvo efecto y el TIME_WAIT estaba evitando una nueva conexión en el mismo puerto con error:[Errno 10048]: Address already in use. Only one usage of each socket address (protocol/IP address/port) is normally permitted
Finalmente usé la solución para permitir que el sistema operativo elija el puerto en sí, luego se usa otro puerto si el precedente todavía está en TIME_WAIT.
Yo reemplacé:
con:
self._socket.bind((guest, 0))
Como se indicó en la documentación del socket de python de una dirección tcp:
fuente
otra solución, en el entorno de desarrollo, por supuesto, es matar el proceso usándolo, por ejemplo
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
fuente
Sé que ya ha aceptado una respuesta, pero creo que el problema tiene que ver con llamar a bind () en un socket de cliente. Esto podría estar bien, pero bind () y shutdown () no parecen funcionar bien juntos. Además, SO_REUSEADDR se usa generalmente con conectores de escucha. es decir, en el lado del servidor.
Debería pasar una ip / puerto para conectar (). Me gusta esto:
comSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) comSocket.connect(('', 5555))
No llame a bind (), no configure SO_REUSEADDR.
fuente
bind()
y noshutdown()
tienen exactamente nada que ver el uno con el otro, y la sugerencia de que "no parecen jugar bien juntos" no tiene fundamento. Te has perdido la parte donde necesita usar el puerto local 5555.Creo que la mejor manera es simplemente matar el proceso en ese puerto, escribiendo en la terminal
fuser -k [PORT NUMBER]/tcp
, por ejemplofuser -k 5001/tcp
.fuente