2012-06-14 101 views
0

假設您有一個相當基本的客戶端/服務器代碼,其中每個客戶端創建三個線程,並且多個客戶端可以一次連接。我希望服務器等待傳入連接,並且一旦它開始獲得連接,運行直到沒有更多線程運行,然後退出。代碼與以下類似。 (即,而不是服務器「永遠服務」,我希望它在所有線程完成後退出)。線程完成時退出的多線程Python服務器

編輯:我希望服務器等待傳入的連接。一旦連接開始,它應該保持接受連接,直到沒有線程保持運行,然後退出。這些連接將有點零星。

import socket 
import threading 

# Our thread class: 
class ClientThread (threading.Thread): 

    # Override Thread's __init__ method to accept the parameters needed: 
    def __init__ (self, channel, details): 

     self.channel = channel 
     self.details = details 
     threading.Thread.__init__ (self) 

    def run (self): 

     print 'Received connection:', self.details [ 0 ] 
     self.channel.send ('hello from server') 
     for x in xrange (10): 
     print self.channel.recv (1024) 
     self.channel.close() 
     print 'Closed connection:', self.details [ 0 ] 

# Set up the server: 
server = socket.socket (socket.AF_INET, socket.SOCK_STREAM) 
server.bind (('', 2727)) 
server.listen (5) 

# Have the server serve "forever": 
while True: 
    channel, details = server.accept() 
    ClientThread (channel, details).start() 
+0

如果服務器永遠在使用,在決定關閉之前應該如何知道應該有多少連接?如果3個連接立即進入,然後沒有任何事情發生,服務器可能會開始等待那3個。但是第四個連接可能會進來。您是否從第一個連接進入的時候開始說,您希望服務器檢測到即時沒有更多的客戶端線程處於活動狀態並關閉?它總是等待100%的空閒時刻去死? – jdi

+0

是的,確切地說,一旦空閒它就可以死亡,但在此之前它應該等到第一個連接或兩個連接進入。 –

回答

1

根據您的意見,您正在尋找的是在第一次連接到服務器之後開始計算連接,並在沒有更多現有連接時終止服務器。

當前無限while循環的直接問題是每個accept()上的塊。所以無論如何,它總是會等待另一個連接。你將不得不從其他線程中斷它以使其脫離該循環。但另一個解決方案是讓事件循環變大,而接受新連接的行爲只是其中的一部分。該循環也應該檢查一個條件退出。

這個例子只有一個可能的方法。它使用Queue.Queue來協調工作臺。

import socket 
import threading 
import select 
from Queue import Queue 

class ClientThread (threading.Thread): 

    def __init__ (self, channel, details, queue=None): 
     self.channel = channel 
     self.details = details 
     self.queue = queue 
     threading.Thread.__init__ (self) 

    def run (self): 

     if self.queue: 
     self.queue.put(1) 

     print 'Received connection:', self.details [ 0 ] 
     self.channel.send ('hello from server') 
     for x in xrange (10): 
     print self.channel.recv (1024) 
     self.channel.close() 
     print 'Closed connection:', self.details [ 0 ] 

     if self.queue: 
     self.queue.get_nowait() 

# Set up the server: 
server = socket.socket (socket.AF_INET, socket.SOCK_STREAM) 
server.bind (('', 2727)) 
server.listen (5) 

rlist = [server] 
work_queue = Queue() 

def accept_client(): 
    channel, details = server.accept() 
    ClientThread (channel, details, work_queue).start() 

accept_client() 

while not work_queue.empty(): 
    server_ready, _, _ = select.select(rlist,[],[], .25) 
    if server in server_ready: 
     accept_client() 

print "Shutting down" 
server.close() 
print "Exiting" 

我們使用select.select作爲一種簡單的方法來檢測你的服務器套接字活動,而且還帶有超時。如果服務器準備就緒,那麼我們接受新的連接。如果達到.25秒超時,我們只需再次循環並等待。

您將看到我們創建了一個隊列並不斷檢查它是否爲空。隊列被傳遞到每個線程。當一個線程啓動時,它會將一些工作記錄到隊列中。數據是任意的。只是一面旗幟。線程完成後,它將從隊列中清除該項目。結果是,在收到第一個連接之後,隊列不再爲空,並且循環將繼續運行。如果在任何時候隊列變空(因爲所有當前線程都已經完成),則循環將中斷並且服務器將關閉。

+0

哇,謝謝!看起來我有一些工作要做,我有一種感覺,我應該利用select和queue模塊,但我不知道從哪裏開始。 –

+0

@COpython:亞,就像我說的,這只是一種方法。您可以通過不使用隊列的方式來完成此操作。 'select.select'非常便攜,所以也很有幫助。 – jdi

0

如果breakwhile循環,該進程將等到所有的ClientThreads退出,然後將終止。

這將工作,因爲客戶端線程是非守護進程。 有關詳細信息,請參閱threading.Thread.daemon

+0

hmm,試過這個,但是在完成連接的第一個客戶端之後它會中斷。即只有在完全相同的時間連接所有線程的情況下,這纔會起作用。 –

+0

@COpython:這意味着主循環停止時只有一個客戶端線程。我想你應該在什麼時候讓流程停止的時候澄清你的需求。 – NPE

+0

我相信你認爲服務器在接收到連接後會中斷,我正在尋找的是在線程結束後退出。 –