2010-05-26 28 views
10

Ho un thread principale che attende la connessione. Genera thread client che rispecchieranno la risposta dal client (telnet in questo caso). Ma dimmi che voglio chiudere tutti i socket e tutti i thread dopo un po 'di tempo, come dopo 1 connessione. Come dovrei fare? Se faccio clientSocket.close() dal thread principale, non smetterà di fare il recv. Si fermerà solo se prima invierò qualcosa tramite telnet, quindi fallirà eseguendo ulteriori mandate e recvs.Come interrompere un socket.recv() da un altro thread in Python

mio aspetto codice come questo:

# 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

Non è possibile farlo con i thread come CPython ha il Global Interpreter Lock. http://docs.python.org/c-api/init.html#threads – badp

risposta

0

non sono sicuro, ma si può forse guardare in coppie di socket

+0

Non penso che mi aiuterà. Penso che l'uso di socketpair sia principalmente per IPC. Non voglio comunicare con il thread, questo renderà il thread client in attesa dell'input dal thread principale. Non penso che risolverà il mio problema. –

5

Non so se è possibile fare quello che stai chiedendo, ma non dovrebbe essere necessario. Basta non leggere dalla presa se non c'è nulla da leggere; utilizzare select.select per verificare il socket per i dati.

cambiamento:

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

a qualcosa di più simile a questo:

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

EDIT: Se si vuole evitare la possibilità che il socket è stato chiuso, cattura 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

Ma la stessa cosa accadrà con select. Se chiudo il socket, si lamenterà del descrittore di file non valido quando si seleziona select.select(). Ho visto che la tua soluzione è apparsa prima di pubblicare la mia soluzione. Cosa pensi del modo in cui l'ho risolto lì, usando i timeout? –

+0

Ho provato lo stesso con selezionare ora. Funziona bene come con i timeout, ma solo se chiudo immediatamente la presa dopo aver avviato il thread. Se faccio un time.sleep (1) fallirà. –

2

Ho trovato una soluzione utilizzando i timeout. Che interrompere il recv (in realtà prima del timeout è scaduto che è bello):

# 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

In C# c'è un metodo molto utile come ** WaitAny ** che sto usando esattamente per questo scopo (per interrompere l'attesa dei dati dal socket se c'è un altro evento con cui lavorare). Python manca davvero di tale funzionalità :( Votato per la tua variante Ma penso che carichi CPU un po 'se ci sono molti thread (e timeout è solo un secondo lungo) ... – sunsay

12

So che questo è un vecchio filo e che Samuel probabilmente risolto il suo problema molto tempo fa. Tuttavia, ho avuto lo stesso problema e ho trovato questo post mentre googleing. Ho trovato una soluzione e penso che valga la pena aggiungere.

È possibile utilizzare il metodo di spegnimento sulla classe socket. Può prevenire ulteriori mandi, riceve o entrambi.

socket.shutdown (socket.SHUT_WR)

L'impedisce futuro sopra invia, come esempio.

See Python docs for more info.

+0

Funziona per me, grazie! –

1

Devo scusarmi per i commenti qui sotto. Il precedente commento di @Matt Anderson funziona. Avevo commesso un errore durante la prova che ha portato al mio post qui sotto.

L'utilizzo del timeout non è una soluzione ottimale. Può sembrare che svegliarsi per un istante e poi riaddormentarsi non sia un grosso problema, ma ho visto che ciò influenza notevolmente le prestazioni di un'applicazione. Hai un'operazione che per la maggior parte vuole bloccare fino a quando i dati sono disponibili e quindi dormire per sempre. Tuttavia, se si vuole abortire per qualche motivo, come chiudere la propria applicazione, il trucco è come uscire. Per i socket, è possibile utilizzare select e listen su due socket. Il tuo principale e uno speciale di spegnimento. Creare uno shutdown è un po 'un problema. Devi crearlo. Devi prendere la presa di ascolto per accettarla. Devi tenere traccia di entrambe le estremità di questa pipa. Ho lo stesso problema con la classe Synchronized Queue. Tuttavia, è possibile inserire almeno un oggetto fittizio nella coda per riattivare get().Ciò richiede che l'oggetto fittizio non assomigli ai tuoi dati normali. A volte vorrei che Python avesse qualcosa come l'API di Windows WaitForMultipleObjects.
Problemi correlati