2010-05-26 78 views
10

我有一个等待连接的主线程。它会产生客户端线程来回应来自客户端的响应(在这种情况下是telnet)。但是,说一段时间后,我想关闭所有套接字和所有线程,例如1次连接之后。我该怎么办?如果我从主线程执行clientSocket.close(),它不会停止执行recv。只有当我第一次通过telnet发送一些东西时,它才会停止,然后它会进行进一步的发送和recv失败。如何从Python中的另一个线程中止socket.recv()

我的代码如下所示:

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

由于CPython具有全局解释器锁定,因此无法使用线程完成此操作。 http://docs.python.org/c-api/init.html#threads – badp 2010-05-26 18:03:23

回答

0

我不知道,但可以或许考虑插座对

+0

我不认为这会对我有帮助。我认为使用socketpair主要是针对IPC。我不想与线程通信,这将使客户端线程等待来自主线程的输入。它不认为这将解决我的问题。 – 2010-05-26 17:44:49

5

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

但是select会发生同样的情况。如果关闭套接字,它会在执行select.select()时抱怨坏文件描述符。我看到您的解决方案出现在我发布我的解决方案之前。您如何看待我在那里解决问题的方式,使用超时? – 2010-05-26 18:44:17

+0

我现在选择了同样的选项。它的工作原理与超时一样好,但只有在我启动线程后立即关闭套接字。如果我做一个time.sleep(1)它会失败。 – 2010-05-26 18:56:27

2

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

在C#中有一个非常有用的方法,例如** WaitAny **,我一直在使用它来完成此目的(如果有另一个事件可以使用,则停止从套接字等待数据)。 Python真的缺乏这样的功能:( 为你的变种投了票 但我认为它加载CPU有点多,如果有很多线程(和超时只有一秒长)... – sunsay 2013-07-26 04:00:31

12

我知道这是一个古老的线程和萨穆埃尔可能固定他的问题在很久以前。但是,我遇到了同样的问题,并在google'ing时遇到此帖子。找到一个解决方案,并认为值得添加。

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

socket.shutdown(socket.SHUT_WR)

上述防止未来发送,作为一个例子。

See Python docs for more info.

+0

适合我,谢谢! – 2012-12-08 13:50:53

1

我必须对下面的评论道歉。由@Matt Anderson作品较早的评论。试用时我犯了一个错误,导致我的帖子在下面。

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