【问题标题】:How do I abort a socket.recv() from another thread in Python如何从 Python 中的另一个线程中止 socket.recv()
【发布时间】:2011-02-24 07:15:24
【问题描述】:

我有一个等待连接的主线程。它产生客户端线程,这些线程将回显来自客户端的响应(在本例中为 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()

【问题讨论】:

标签: python multithreading sockets recv


【解决方案1】:

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

使用超时不是一个很好的解决方案。看起来一瞬间醒来然后重新入睡没什么大不了的,但我已经看到它极大地影响了应用程序的性能。您有一个操作在大多数情况下想要阻止,直到数据可用并因此永远休眠。但是,如果您出于某种原因想要中止,例如关闭您的应用程序,那么诀窍就是如何退出。对于套接字,您可以在两个套接字上使用选择和侦听。您的主要设备和特殊关机设备。创建关机虽然有点痛苦。你必须创造它。你必须让监听套接字接受它。您必须跟踪此管道的两端。我对同步队列类有同样的问题。但是,您至少可以在队列中插入一个虚拟对象以唤醒 get()。这要求虚拟对象看起来不像您的正常数据。我有时希望 Python 有类似 Windows API WaitForMultipleObjects 的东西。

【讨论】:

    【解决方案2】:

    我知道这是一个旧线程,Samuel 可能很久以前就解决了他的问题。但是,我遇到了同样的问题,并在谷歌搜索时遇到了这篇文章。找到了解决办法,觉得值得补充。

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

    socket.shutdown(socket.SHUT_WR)

    例如,以上内容可防止将来发送。

    See Python docs for more info.

    【讨论】:

      【解决方案3】:

      我不知道是否可以按照您的要求进行操作,但这不是必需的。如果没有可读取的内容,请不要从套接字读取;使用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)
      

      【讨论】:

      • 但是 select 也会发生同样的事情。如果我关闭套接字,它会在执行 select.select() 时抱怨文件描述符错误。在发布我的解决方案之前,我看到您的解决方案出现了。您如何看待我在那里使用超时解决问题的方式?
      • 我现在用 select 尝试了同样的方法。它与超时一样好,但前提是我在启动线程后立即关闭套接字。如果我执行 time.sleep(1) 它将失败。
      【解决方案4】:

      我找到了使用超时的解决方案。这将中断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()
      

      【讨论】:

      • 在 C# 中有一个非常有用的方法,例如 WaitAny 我一直在使用它来达到这个目的(如果有另一个事件要处理,则停止等待来自套接字的数据)。 Python 真的缺乏这样的功能:( 为你的变体投票。但我认为如果有很多线程(超时只有一秒钟)它会稍微加载 CPU...
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-29
      • 1970-01-01
      • 2014-11-29
      • 1970-01-01
      相关资源
      最近更新 更多