如何从Python中的另一个线程中止socket.recv()

时间:2010-05-26 17:04:46

标签: python multithreading sockets recv

我有一个等待连接的主线程。它产生的客户端线程将回应客户端的响应(在本例中为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 个答案:

答案 0 :(得分:16)

我知道这是一个老话题,塞缪尔很可能早就解决了他的问题。但是,我有同样的问题,并在google'ing时遇到了这篇文章。找到了解决方案并认为值得添加。

您可以在套接字类上使用shutdown方法。它可以防止进一步发送,接收或两者兼而有之。

  

socket.shutdown(socket.SHUT_WR)

以上是为了防止将来的发送,例如。

See Python docs for more info.

答案 1 :(得分:6)

我不知道你是否可以做你所要求的,但它不应该是必要的。如果没有什么可读的,请不要从套接字读取;使用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)

答案 2 :(得分: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()

答案 3 :(得分:2)

我必须为下面的评论道歉。 @Matt Anderson的早期评论有效。尝试时我犯了一个错误导致我的帖子在下面。

使用超时不是一个很好的解决方案。看起来似乎只是瞬间醒来然后再回到睡眠状态并不是什么大不了的事,但我已经看到它会极大地影响应用程序的性能。您有一个操作,大部分都希望阻止,直到数据可用,从而永远睡眠。但是,如果你想因为某些原因而中止,比如关闭你的应用程序,那么诀窍是如何离开。对于套接字,您可以使用select和listen两个套接字。你的主要,一个特殊的关闭。创建关闭一个虽然有点痛苦。你必须创建它。您必须让侦听套接字接受它。你必须跟踪这个管道的两端。我对Synchronized Queue类有同样的问题。但是,您可以至少将一个虚拟对象插入队列以唤醒get()。这要求虚拟对象看起来不像普通数据。我有时希望Python有类似Windows API WaitForMultipleObjects的东西。