[Python-es] ¿Como cierro correctamente un hilo-servidor de sockets?
Sergio Martín
sergiomartinj en gmail.com
Vie Ago 26 02:21:49 CEST 2011
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
<sergiomartinj en gmail.com> escribió:
> 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()
>
Más información sobre la lista de distribución Python-es