2009-07-18 10 views
8

He escrito una clase de python muy simple que espera conexiones en un socket. La intención es incluir esta clase en una aplicación existente y enviar datos de manera asincrónica a los clientes que se conectan.Python socket accept blocks - impide que la aplicación se cierre

El problema es que al esperar en un socket.accept(), no puedo finalizar mi aplicación presionando ctrl-c. Tampoco puedo detectar cuando mi clase está fuera del alcance y notificar que termine.

Lo ideal es que la siguiente aplicación se cierre después de que caduque el time.sleep (4). Como puede ver a continuación, traté de usar seleccionar, pero esto también impide que la aplicación responda a ctrl-c. Si pudiera detectar que la variable 'a' ha salido del alcance en el método principal, podría establecer el indicador de abandono (y reducir el tiempo de espera en select para que responda).

¿Alguna idea?

gracias


import sys 
import socket 
import threading 
import time 
import select 

class Server(threading.Thread): 
    def __init__(self, i_port): 
     threading.Thread.__init__(self) 
     self.quitting = False 
     self.serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
     self.serversocket.bind((socket.gethostname(), i_port)) 
     self.serversocket.listen(5) 
     self.start() 

    def run(self): 
     # Wait for connection 
     while not self.quitting: 
      rr,rw,err = select.select([self.serversocket],[],[], 20) 
      if rr: 
       (clientsocket, address) = self.serversocket.accept() 
       clientsocket.close() 

def main(): 
    a = Server(6543) 
    time.sleep(4) 

if __name__=='__main__': 
    main() 

Respuesta

9

Añadir self.setDaemon(True) a la __init__self.start() antes.

(En Python 2.6 y posterior, se prefiere self.daemon = True).

La idea clave es explicado here:

Todo el programa termina Python cuando sin hilos no demonio se dejan vivos.

Entonces, necesitas hacer "demonios" de esos hilos que no deberían mantener todo el proceso vivo solo por estar vivos ellos mismos. El hilo principal siempre es no demonio, por cierto.

+0

Gracias por eso, funcionó. Intenté self.daemon = True pero eso no hizo nada (estoy ejecutando python 2.5 en Win32). Sin embargo, cambiarlo a self.setDaemon (True) hizo el truco. gracias! –

4

No recomiendo la función setDaemon para el apagado normal. Es descuidado; en lugar de tener un camino de cierre limpio para los hilos, simplemente elimina el hilo sin posibilidad de limpieza. Es bueno configurarlo, para que su programa no se quede atascado si el hilo principal se cierra de forma inesperada, pero no es una buena ruta de apagado normal a excepción de los ataques rápidos.

import sys, os, socket, threading, time, select 

class Server(threading.Thread): 
    def __init__(self, i_port): 
     threading.Thread.__init__(self) 
     self.setDaemon(True) 
     self.quitting = False 
     self.serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
     self.serversocket.bind((socket.gethostname(), i_port)) 
     self.serversocket.listen(5) 
     self.start() 

    def shutdown(self): 
     if self.quitting: 
      return 

     self.quitting = True 
     self.join() 

    def run(self): 
     # Wait for connection 
     while not self.quitting: 
      rr,rw,err = select.select([self.serversocket],[],[], 1) 
      print rr 
      if rr: 
       (clientsocket, address) = self.serversocket.accept() 
       clientsocket.close() 

     print "shutting down" 
     self.serversocket.close() 

def main(): 
    a = Server(6543) 
    try: 
     time.sleep(4) 
    finally: 
     a.shutdown() 

if __name__=='__main__': 
    main() 

Tenga en cuenta que esto retrasará hasta un segundo después de llamar a shutdown(), que es el mal comportamiento. Esto normalmente es fácil de solucionar: cree un canal de activación() en el que pueda escribir, e inclúyalo en la selección; pero aunque esto es muy básico, no pude encontrar ninguna forma de hacerlo en Python. (os.pipe() devuelve los descriptores de archivo, no los objetos de archivo en los que podemos escribir). No he profundizado, ya que es tangencial a la pregunta.

+0

Puede ajustar un descriptor de archivo en un objeto de archivo de Python con os.fdopen. Pero no estoy de acuerdo en que se justifique algo más que setDaemon cuando no se necesita una limpieza particular en el hilo, es solo una complicación pequeña pero prescindible. –

+0

Probé un enfoque muy similar a este: mi método ShutDown simplemente estableció un indicador IsQuitting y se conectó al socket en cuestión, desbloqueando así la llamada accept(), permitiendo que el hilo se cierre. Sin embargo, depende del usuario que llame a ShutDown(). Gracias por la sugerencia, sin embargo. –

+0

Para algo más grande que un simple hack, vas a necesitar poder cerrar un hilo sin salir de todo el proceso. Incluso un servidor simple que desee registrar el "cierre exitoso" tendrá que hacer esto, o de todos modos aceptará conexiones después de cerrar la sesión. –

Cuestiones relacionadas