使Python套接字服务器更高效

Ben*_*lls 2 python sockets multithreading

我对使用套接字和多线程编程的经验很少,所以为了了解更多,我决定看看我是否可以将一个小的python套接字服务器连接起来为聊天室供电.我最终让它工作得很好,但后来我注意到当我在后台运行时,我的服务器CPU使用率飙升超过100%.

这是我的完整代码: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)
Run Code Online (Sandbox Code Playgroud)

Ale*_*lli 6

您的代码中有几种可能的竞争条件,但它们会威胁正确性而不是性能:修复它们,例如通过锁定肯定不会提高性能.

相反,我会专注于你认为那些线程正在做的好事 - 因为你的代码的核心是一个select.select调用,为什么不专注于那个......以及一个完全异步,因此更有效...服务器而不是将一些任务反弹到基本上只是开销的线程.在某些输入准备就绪时读取(正如您所做的那样),在某个套接字准备好输出时写入,&c - 它比当前的线程和异步混合更简单,更快.

直接在异步服务器上编程select.select是一种相当低级的方法,虽然它具有指导性,但它并不适合生产.考虑使用asyncoreasynchatPython标准库模块的适度更高的抽象水平,或twisted第三方包一个更高的提升(包括更有效的手段,而不是老来实现底层的"反应堆"设计模式的能力select-有poll,kqueues等,取决于你所使用的操作系统,Twisted可以让你根据你的平台选择实现,同时保持相同的Reactor接口).

我想,我盖住这些各种可能性体面,如果简明,在"服务器端套接字"果壳中的第二版的Python章 - 你可以通过获取试用订阅O'Reilly的"野生动物园在线"获得免费在线现场,或(通过查找和使用许多盗版网站主办的书籍(假设当然盗版复制你不想用得到它"一切合法和适当" ;-)花钱为它的一个非法;-).我想你无论如何都可以免费下载带有O'Reilly网站所有示例代码的zip文件.

  • 还有一个可以免费看书的地方……嗯……我想他们称之为“图书馆”。 (2认同)