2010-05-26 63 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的東西。
相關問題