[Python-es] ¿Como cierro correctamente un hilo-servidor de sockets?

626 views
Skip to first unread message

Sergio Martín

unread,
Aug 25, 2011, 8:17:22 PM8/25/11
to pyth...@python.org
Tengo un script en el que, primero, ejecuto un servidor de sockets en
un hilo, y cada conexión que reciba, genera su propio hilo.
El problema viene cuando intento salirme del programa mediante una
excepción KeyboardInterrupt controlada, funciona bien si no ha habido
ninguna conexión al socket-servidor, pero si me salgo del programa una
vez que he recibido alguna conexión, y, a continuación ejecuto el
programa de nuevo, me sale un "socket.error: [Errno 48] Address
already in use", como si no hubiese cerrado el socket del servidor
correctamente, teniéndome que esperar un rato hasta que se libere el
puerto.
Tengo controladas dos situaciones una que desde el cliente telnet se
pase el comando "quit", con lo que cierro el socket del cliente, y
otra cuando se pierde la conexión con el cliente sin introducir el
comando "quit"
El error solo me lo lanza cuando he salido por medio del "quit".

Aviso que está escrito en python3, y se que hay mejores formas de
hacer esto en vez de usar hilos, como el módulo twisted (sin
compatibilidad python3) o el asyncore, pero solo tengo planeado
recibir un par de conexiones simultáneas por lo que no se generarán
muchos hilos.

Pongo una versión simplificada del programa, con solo lo básico para
ilustrar el problema:

#! /usr/bin/env python3

import threading
import socket
import sys
import time

class TelnetServer(threading.Thread):

def __init__(self):
threading.Thread.__init__(self)
self.socketserver = socket.socket()
self.socketserver.bind(('', 9999))
self.socketserver.listen(5)

def run(self):
print('Servidor en marcha')
while True:
socketclient, addr = self.socketserver.accept()
client = TelnetClient(socketclient, addr)
client.start()

def close(self):
print('Servidor detenido')

class TelnetClient(threading.Thread):

def __init__(self, socketclient, addr):
threading.Thread.__init__(self)
self.socketclient = socketclient
self.addr = addr

def run(self):
print('Conexión: %s:%s' % (self.addr[0], self.addr[1]))
while True:
try:
command, args = self.prompt()
except socket.error:
self.close()
break

if command == None:
pass
elif command == 'quit':
self.close()
break
else:
self.send('Comando desconocido\n')

def send(self, msg):
self.socketclient.send(msg.encode('utf8'))

def recv(self):
return self.socketclient.recv(1024).decode('utf8')[:-2]

def prompt(self):
try:
self.send('prompt> ')
recv_list = self.recv().split()
return recv_list[0].lower(), recv_list[1:]
except IndexError:
return None, []

def close(self):
self.socketclient.close()
print('Desconexión: %s:%s' % (self.addr[0], self.addr[1]))

if __name__ == '__main__':
try:
telnetserver = TelnetServer()
telnetserver.daemon = True
telnetserver.start()

while True:
time.sleep(100)

except KeyboardInterrupt:
telnetserver.close()
sys.exit()
_______________________________________________
Python-es mailing list
Pyth...@python.org
http://mail.python.org/mailman/listinfo/python-es
FAQ: http://python-es-faq.wikidot.com/

Sergio Martín

unread,
Aug 25, 2011, 8:21:49 PM8/25/11
to pyth...@python.org
Comentar que aunque añada la línea:
self.socketserver.close()
en el método close() de la clase TelnetServer el resultado es el mismo.

El día 26 de agosto de 2011 02:17, Sergio Martín
<sergio...@gmail.com> escribió:

chakalinux

unread,
Aug 26, 2011, 9:10:57 AM8/26/11
to La lista de python en castellano
En la interrupcion KeyboardInterrupt tienes que cerrar los hilos para que no se te quede el mismo en CLOSE_WAIT que en ciertos casos puede durar bastante en liberarse.

De todas maneras yo te recomiendo que uses la librería select o asyncore para manejar socket's, intenta evitar cuando puedas sockets y threading

Sergio Martín

unread,
Aug 26, 2011, 2:25:43 PM8/26/11
to La lista de python en castellano
Pero al cerrar el socket del servidor (como comento en mi segundo mensaje) ¿no debería liberarse el puerto?
Por otro lado ¿como puedo cerrar el hilo si lo tengo en espera de un cliente? ¿Hay alguna otra forma aparte de salir del bucle infinito que tengo?

gerardo Juarez

unread,
Aug 26, 2011, 3:53:06 PM8/26/11
to La lista de python en castellano
Efectivamente Sergio, es el mismo comportamiento que yo he observado: la
aplicación cierra el socket, termina y aún así, pasan algunos segundos
para que el puerto quede disponible nuevamente. No me parece que sea un
problema directamente atribuible a tu aplicación, sino más bien a la
forma en que el sistema libera -o registra- que los puertos han sido
liberados. Habría que informarse cómo está implementada la función
'restart' de los servicios de Linux, por ejemplo, porque allí terminan
un servicio y lo reinician tan pronto como es posible. De qué modo
averiguan que ya está disponible el puerto?

Sergio Martín wrote:
> Pero al cerrar el socket del servidor (como comento en mi segundo
> mensaje) ¿no debería liberarse el puerto?
> Por otro lado ¿como puedo cerrar el hilo si lo tengo en espera de un
> cliente? ¿Hay alguna otra forma aparte de salir del bucle infinito que
> tengo?
>
> El 26 de agosto de 2011 15:10, chakalinux <chaka...@gmail.com

> <mailto:chaka...@gmail.com>> escribió:


>
> En la interrupcion KeyboardInterrupt tienes que cerrar los hilos
> para que no se te quede el mismo en CLOSE_WAIT que en ciertos
> casos puede durar bastante en liberarse.
>
> De todas maneras yo te recomiendo que uses la librería select o
> asyncore para manejar socket's, intenta evitar cuando puedas
> sockets y threading
>
> El 26 de agosto de 2011 02:21, Sergio Martín

> <sergio...@gmail.com <mailto:sergio...@gmail.com>> escribió:


>
> Comentar que aunque añada la línea:
> self.socketserver.close()
> en el método close() de la clase TelnetServer el resultado es
> el mismo.
>
> El día 26 de agosto de 2011 02:17, Sergio Martín

> <sergio...@gmail.com <mailto:sergio...@gmail.com>>

> Pyth...@python.org <mailto:Pyth...@python.org>


> http://mail.python.org/mailman/listinfo/python-es
> FAQ: http://python-es-faq.wikidot.com/
>
>
>
> _______________________________________________
> Python-es mailing list

> Pyth...@python.org <mailto:Pyth...@python.org>

> ------------------------------------------------------------------------

Nekmo

unread,
Aug 26, 2011, 10:39:56 PM8/26/11
to La lista de python en castellano
La mejor solución a mi parecer, es modificar el timeout. A parte, si
usáis TCP, a veces solo queda esperar a que se libere... es el
problema de dicho protocolo :P

Un cordial saludo:
-- Nekmo.

Sitio web: http://nekmo.com
Dirección de contacto: cont...@nekmo.com
XMPP/Jabber: cont...@nekmo.com
Identi.ca: http://identi.ca/nekmo
Diaspora: Nekmo
Google+: Nekmo Com

El día 26 de agosto de 2011 21:53, gerardo Juarez
<gerard...@buyteknet.info> escribió:

Reply all
Reply to author
Forward
0 new messages