2010-05-26 14 views
10

Tengo un hilo principal que espera la conexión. Genera hilos de clientes que harán eco de la respuesta del cliente (telnet en este caso). Pero diga que quiero cerrar todos los sockets y todos los hilos después de un tiempo, como después de 1 conexión. ¿Cómo lo haría? Si hago clientSocket.close() desde el hilo principal, no dejará de hacer el recv. Solo se detendrá si primero envío algo a través de telnet, luego fallará haciendo más envíos y recvs.Cómo cancelo un socket.recv() desde otro hilo en Python

Mi mirada código como este:

# Echo server program 
import socket 
from threading import Thread 
import time 

class ClientThread(Thread): 
    def __init__(self, clientSocket): 
      Thread.__init__(self) 
      self.clientSocket = clientSocket 

    def run(self): 
      while 1: 
        try: 
          # It will hang here, even if I do close on the socket 
          data = self.clientSocket.recv(1024) 
          print "Got data: ", data 
          self.clientSocket.send(data) 
        except: 
          break 

      self.clientSocket.close() 

HOST = '' 
PORT = 6000 
serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
serverSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
serverSocket.bind((HOST, PORT)) 
serverSocket.listen(1) 

clientSocket, addr = serverSocket.accept() 
print 'Got a new connection from: ', addr 
clientThread = ClientThread(clientSocket) 
clientThread.start() 

time.sleep(1) 

# This won't make the recv in the clientThread to stop immediately, 
# nor will it generate an exception 
clientSocket.close() 
+0

No puede hacerlo con subprocesos ya que CPython tiene el bloqueo de Intérprete global. http://docs.python.org/c-api/init.html#threads – badp

Respuesta

0

no estoy seguro, pero que tal vez puede mirar en parejas de conectores

+0

No creo que eso me ayude. Creo que usar socketpair es principalmente para IPC. No quiero comunicarme con el hilo, eso hará que el hilo del cliente espere la entrada del hilo principal. No creo que eso solucione mi problema. –

5

No sé si es posible hacer lo que estás pidiendo, pero no debería ser necesario. Simplemente no lea desde el socket si no hay nada que leer; use select.select para verificar los datos del socket.

cambio:

data = self.clientSocket.recv(1024) 
print "Got data: ", data 
self.clientSocket.send(data) 

a algo más parecido a esto:

r, _, _ = select.select([self.clientSocket], [], []) 
if r: 
    data = self.clientSocket.recv(1024) 
    print "Got data: ", data 
    self.clientSocket.send(data) 

EDIT: Si desea protegerse contra la posibilidad de que el enchufe se ha cerrado, la captura socket.error.

do_read = False 
try: 
    r, _, _ = select.select([self.clientSocket], [], []) 
    do_read = bool(r) 
except socket.error: 
    pass 
if do_read: 
    data = self.clientSocket.recv(1024) 
    print "Got data: ", data 
    self.clientSocket.send(data) 
+1

Pero lo mismo sucederá con select. Si cierro el socket, se quejará del descriptor de archivo incorrecto al seleccionar select.select(). Vi que tu solución apareció antes de publicar mi solución. ¿Qué piensas de la forma en que lo resolví allí, usando tiempos de espera? –

+0

Intenté lo mismo con seleccionar ahora. Funciona tan bien como con tiempos de espera, pero solo si cierro el socket inmediatamente después de iniciar el hilo. Si hago un time.sleep (1), fallará. –

2

Encontré una solución usando tiempos de espera. Eso va a interrumpir el recv (en realidad antes de que el tiempo de espera ha expirado lo cual es bueno):

# Echo server program 
import socket 
from threading import Thread 
import time 


class ClientThread(Thread): 
    def __init__(self, clientSocke): 
     Thread.__init__(self) 
     self.clientSocket = clientSocket 

    def run(self): 
     while 1: 
      try: 
       data = self.clientSocket.recv(1024) 
       print "Got data: ", data 
       self.clientSocket.send(data) 
      except socket.timeout: 
       # If it was a timeout, we want to continue with recv 
       continue 
      except: 
       break 

     self.clientSocket.close() 

HOST = '' 
PORT = 6000 
serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
serverSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
serverSocket.bind((HOST, PORT)) 
serverSocket.listen(1) 

clientSocket, addr = serverSocket.accept() 
clientSocket.settimeout(1) 

print 'Got a new connection from: ', addr 
clientThread = ClientThread(clientSocket) 
clientThread.start() 

# Close it down immediatly 
clientSocket.close() 
+0

En C# hay un método muy útil como ** WaitAny ** que he estado utilizando exactamente para este fin (para dejar de esperar datos del socket si hay otro evento para trabajar). Python realmente carece de esa funcionalidad :( Votado para tu variante. Pero creo que carga un poco la CPU si hay muchos hilos (y el tiempo de espera es de solo un segundo) ... – sunsay

12

Sé que esto es un hilo de edad y que, probablemente, Samuel fijó su tema hace mucho tiempo. Sin embargo, tuve el mismo problema y encontré esta publicación mientras navegaba por google. Encontré una solución y creo que vale la pena agregarla.

Puede utilizar el método de apagado en la clase de socket. Puede evitar más envíos, recepciones o ambos.

socket.shutdown (socket.SHUT_WR)

El previene futuras anteriormente envía, como un ejemplo.

See Python docs for more info.

+0

¡Funciona para mí, gracias! –

1

Debo disculparme por los comentarios a continuación. El comentario anterior por @Matt Anderson funciona. Cometí un error al probarlo, lo que me llevó a mi publicación a continuación.

El uso del tiempo de espera no es una solución muy buena. Puede parecer que despertar por un instante y luego volver a dormir no es gran cosa, pero he visto que afecta en gran medida el rendimiento de una aplicación. Usted tiene una operación que en su mayor parte quiere bloquear hasta que haya datos disponibles y, por lo tanto, duerma para siempre. Sin embargo, si quiere abortar por alguna razón, como cerrar su aplicación, entonces el truco es cómo salir. Para enchufes, puede usar seleccionar y escuchar en dos enchufes. Tu primaria, y una parada especial. Sin embargo, crear el shutdown es un poco doloroso. Tienes que crearlo. Tienes que conseguir que el socket de escucha lo acepte. Tienes que hacer un seguimiento de ambos extremos de este tubo. Tengo el mismo problema con la clase Cola sincronizada. Sin embargo, al menos puede insertar un objeto ficticio en la cola para activar el get().Sin embargo, esto requiere que el objeto ficticio no se vea como tus datos normales. A veces me gustaría que Python tuviera algo así como la API de Windows WaitForMultipleObjects.
Cuestiones relacionadas