Как прервать сокет .recv() из другого потока в Python

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

Мой код выглядит так:

# 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()

Ответ 1

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

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

socket.shutdown(socket.SHUT_WR)

Вышеуказанное предотвращает будущие отправки в качестве примера.

Подробнее см. в документах Python.

Ответ 2

Я не знаю, можно ли делать то, что вы просите, но это не обязательно. Просто не читайте из сокета, если читать нечего; используйте 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)

РЕДАКТИРОВАТЬ: Если вы хотите защитить от возможности закрытия сокета, поймите 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)

Ответ 3

Я нашел решение с использованием тайм-аутов. Это приведет к прерыванию recv (фактически до истечения таймаута, который является хорошим):

# 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()

Ответ 4

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

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

Ответ 5

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