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

20 投票
4 回答
27978 浏览
提问于 2025-04-15 23:12

我有一个主线程,它在等待连接。主线程会创建一些客户端线程,这些线程会把客户端(这里是telnet)发来的信息原封不动地返回给它。不过,我想在一段时间后,比如说在接收到一次连接后,关闭所有的套接字和线程。

我该怎么做呢?如果我在主线程中执行 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 个回答

3

我找到了一种使用超时的方法。这种方法可以在接收数据时中断操作(实际上是在超时到期之前,这样挺好的):

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

我不知道你问的这个是否可行,但其实没必要这样做。只要在没有数据可读的时候就别去读取那个套接字;可以用 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)
28

我知道这个话题已经很老了,Samuel可能早就解决了他的问题。不过,我也遇到了同样的问题,在网上搜索时看到了这个帖子。我找到了解决办法,觉得有必要分享一下。

你可以使用套接字类里的shutdown方法。这个方法可以阻止后续的数据发送、接收,或者两者都阻止。

socket.shutdown(socket.SHUT_WR)

上面的代码是用来阻止将来的数据发送的,举个例子。

想了解更多信息,可以查看Python的文档。

撰写回答