2010-03-14 18 views
2

我很少有使用套接字和多線程編程的經驗,所以瞭解更多信息我決定查看是否可以在一起使用一個小型python套接字服務器來爲聊天室供電。我最終得到它工作得很好,但後來我發現當我在後臺運行時,我的服務器的CPU使用率猛增了100%以上。使Python套接字服務器更高效

這是我在全碼:http://gist.github.com/332132

我知道這是一個相當開放式的問題,以便除了剛纔我的代碼幫助請問有什麼好的文章我可以閱讀,可以幫助我瞭解更多關於此?

我全碼:

import select 
import socket 
import sys 
import threading 
from daemon import Daemon 

class Server: 
def __init__(self): 
    self.host = '' 
    self.port = 9998 
    self.backlog = 5 
    self.size = 1024 
    self.server = None 
    self.threads = [] 
    self.send_count = 0 

def open_socket(self): 
    try: 
     self.server = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) 
     self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
     self.server.bind((self.host,self.port)) 
     self.server.listen(5) 
     print "Server Started..." 
    except socket.error, (value,message): 
     if self.server: 
      self.server.close() 
     print "Could not open socket: " + message 
     sys.exit(1) 

def remove_thread(self, t): 
    t.join() 

def send_to_children(self, msg): 
    self.send_count = 0 
    for t in self.threads: 
     t.send_msg(msg) 
    print 'Sent to '+str(self.send_count)+" of "+str(len(self.threads)) 

def run(self): 
    self.open_socket() 
    input = [self.server,sys.stdin] 
    running = 1 
    while running: 
     inputready,outputready,exceptready = select.select(input,[],[]) 

     for s in inputready: 
      if s == self.server: 
       # handle the server socket 
       c = Client(self.server.accept(), self) 
       c.start() 
       self.threads.append(c) 
       print "Num of clients: "+str(len(self.threads)) 

    self.server.close() 
    for c in self.threads: 
     c.join() 

class Client(threading.Thread): 
def __init__(self,(client,address), server): 
    threading.Thread.__init__(self) 
    self.client = client 
    self.address = address 
    self.size = 1024 
    self.server = server 
    self.running = True 

def send_msg(self, msg): 
    if self.running: 
     self.client.send(msg) 
     self.server.send_count += 1 

def run(self): 
    while self.running: 
     data = self.client.recv(self.size) 
     if data: 
      print data 
      self.server.send_to_children(data) 
     else: 
      self.running = False 
      self.server.threads.remove(self) 
      self.client.close() 

""" 
Run Server 
""" 

class DaemonServer(Daemon): 
def run(self): 
    s = Server() 
    s.run() 

if __name__ == "__main__": 
d = DaemonServer('/var/servers/fserver.pid') 
if len(sys.argv) == 2: 
    if 'start' == sys.argv[1]: 
     d.start() 
    elif 'stop' == sys.argv[1]: 
     d.stop() 
    elif 'restart' == sys.argv[1]: 
     d.restart() 
    else: 
     print "Unknown command" 
     sys.exit(2) 
    sys.exit(0) 
else: 
    print "usage: %s start|stop|restart" % sys.argv[0] 
    sys.exit(2) 
+0

代碼不完整,因此很難說它在哪裏(可能)正在等待。你可以 - 也應該 - 在這裏嵌入這麼小的代碼。 – msw 2010-03-14 18:42:00

+0

因爲我不知道你正在使用哪個守護進程庫,我只是把它剪掉,然後做了'Server()。run()'。沒有處理器負載的情況下,它運行良好,3客戶端它似乎沒有處理客戶端分離,我正在調查。我不是一個線程專家,唉。 – msw 2010-03-14 19:16:36

回答

6

有在你的代碼幾種可能的競爭條件,但他們會威脅正確性,而不是性能:例如他們固定通過鎖定絕對不會提高性能。因爲你的代碼的核心是一個select.select的調用,爲什麼不把注意力集中在那個...上,而且是一個完全異步的,因此更重要的是,有效...服務器,而不是將一些任務反彈到基本上只是開銷的線程。當一些輸入準備就緒時(如你所做的那樣)讀取,當某個套接字準備好輸出時寫入,& c - 比當前的線程和異步混合更簡單快捷。

直接在select.select之上編程異步服務器是一個相當低級的方法,儘管它很有啓發性,但它並不真正適合生產。考慮將Python標準庫的asyncoreasynchat模塊用於適當提高抽象級別,或使用twisted第三方軟件包進行更高的提升(包括通過比舊的更有效的方式實現底層「Reactor」設計模式的能力select - 有民意調查,kqueues等,取決於你在哪個操作系統上,Twisted可以讓你根據你的平臺選擇實現,同時保持相同的Reactor界面)。

我想我在一個Nutshell 2nd Ed的Python的「服務器端套接字」一章中簡要地介紹了這些不同的可能性 - 您可以通過試用訂閱O'Reilly's 「Safari在線」網站,或者(非法;-)通過查找和使用託管海盜副本書籍的許多海盜網站之一(當然,假設您不希望花錢購買它「所有合法和適當」 ;-)。無論如何,我認爲你可以自由地下載來自O'Reilly網站的所有示例代碼的zip文件。

+1

還有一個地方可以免費閱讀書籍......嗯..我認爲他們稱之爲「圖書館」。 – 2013-01-04 20:53:22