2010-05-26 1 views
10

У меня есть основной поток, который ждет подключения. Он порождает потоки клиентов, которые будут отражать ответ от клиента (telnet в этом случае). Но скажите, что я хочу закрыть все сокеты и все потоки через некоторое время, например, после 1 соединения. Как мне это сделать? Если я делаю clientSocket.close() из основного потока, это не остановит выполнение recv. Это остановится только в том случае, если я сначала отправлю что-то через telnet, а затем не выполнит дальнейшие отправки и повторы.Как прервать сокет.recv() из другого потока в Python

Мой код выглядит следующим образом:

# 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

Вы не можете делать это с помощью потоков, так как CPython имеет глобальный блокиратор Interpreter. http://docs.python.org/c-api/init.html#threads – badp

ответ

0

Я не уверен, но возможно, вы можете посмотреть в пар сокетов

+0

Я не думаю, что это мне поможет. Я думаю, что использование socketpair в основном для IPC. Я не хочу общаться с потоком, который заставит клиентский поток ждать ввода из основного потока. Он не думает, что это решит мою проблему. –

5

Я не знаю, если это возможно, чтобы делать то, что вы просите, но это не обязательно. Просто не читайте из сокета, если читать нечего; используйте select.select для проверки сокета для данных.

изменение:

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

к чему-то более, как это:

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

EDIT: Если вы хотите, чтобы защититься от возможности того, что сокет был закрыт, уловом 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

Но то же самое произойдет с выбором. Если я закрою сокет, он будет жаловаться на плохой дескриптор файла при выполнении select.select(). Я видел, что ваше решение появилось до того, как я опубликовал свое решение. Что вы думаете о том, как я это решил, используя таймауты? –

+0

Я пробовал то же самое с выбором сейчас. Он работает так же хорошо, как и с таймаутами, но только если я сразу же закрываю сокет после запуска потока. Если я сделаю time.sleep (1), он потерпит неудачу. –

2

Я нашел решение с использованием тайм-аутов. Это прервет ПРИЕМ (на самом деле до того, как истекло время ожидания истекло, и это приятно):

# 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

В C# есть очень полезный метод, например ** WaitAny **, который я использовал именно для этой цели (чтобы остановить ожидающие данные из сокета, если есть другое событие для работы). Python действительно испытывает недостаток в такой функциональности :( Проголосовал за ваш вариант. Но я думаю, что он немного загружает CPU, если есть много потоков (и тайм-аут длится всего одну секунду) ... – sunsay

12

Я знаю, что это старый нить и что Самуэль вероятно устремил вопрос давно. Тем не менее, у меня была такая же проблема, и я столкнулся с этим сообщением во время google'ing. Нашли решение и подумайте, что стоит добавить.

Вы можете использовать метод выключения для класса сокета. Он может предотвратить дальнейшие отправки, получение или и то, и другое.

socket.shutdown (socket.SHUT_WR)

выше предотвращает будущее отправляет, в качестве примера.

See Python docs for more info.

+0

Работает для меня, спасибо! –

1

Я должен извиниться за комментарии ниже. Более ранний комментарий от @Matt Anderson работает. Я ошибся, когда попробовал, что привело к моему сообщению ниже.

Использование тайм-аута не очень хорошее решение. Может показаться, что просыпаться на мгновение, а затем возвращаться спать не имеет большого значения, но я видел, что это сильно влияет на производительность приложения. У вас есть операция, которая по большей части хочет блокировать до тех пор, пока данные не будут доступны, и таким образом спать навсегда. Однако, если вы хотите по какой-то причине отказаться, например, закрыть приложение, то трюк - это как выйти. Для сокетов вы можете использовать select и прослушивать два сокета. Ваш основной и специальный выключение. Создание выключения одного, хотя это немного боль. Вы должны создать его. Вы должны заставить слушающий сокет принять его. Вы должны следить за обоими концами этой трубы. У меня такая же проблема с классом Synchronized Queue. Тем не менее, вы можете по крайней мере вставить фиктивный объект в очередь, чтобы разбудить get().Это требует, чтобы фиктивный объект не выглядел как ваши обычные данные. Я иногда хочу, чтобы у Python было что-то вроде Windows API WaitForMultipleObjects.

 Смежные вопросы

  • Нет связанных вопросов^_^