Как прервать socket.recv () из другого потока в Python - PullRequest
14 голосов
/ 26 мая 2010

У меня есть основной поток, который ждет подключения. Он порождает клиентские потоки, которые будут повторять ответ от клиента (в данном случае telnet). Но скажите, что я хочу закрыть все сокеты и все потоки через некоторое время, например, после 1 соединения.

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

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

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

Ответы [ 4 ]

16 голосов
/ 19 июня 2011

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

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

socket.shutdown (socket.SHUT_WR)

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

См. Документы Python для получения дополнительной информации.

6 голосов
/ 26 мая 2010

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

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

# 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()
2 голосов
/ 14 декабря 2011

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

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