优化简单服务器以支持多个客户端

2 投票
1 回答
502 浏览
提问于 2025-04-17 02:12

我成功地实现了一个简单的服务器,可以同时处理多个客户端:

class ProcessingServer:
    def __init__(self, bindaddress="127.0.0.1", portname=50001, maxqueue=5):
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.bind((bindaddress, portname))
        self.socket.listen(maxqueue)
        self.inputsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.data = []

    def start(self):
        rsocks, wsocks = [], []
        rsocks.append(self.socket)
        rsocks.append(self.inputsocket)
        print "Waiting for connection..."
        self.inputsocket.connect(("192.168.1.1", 1234))
        print "Connected."
        clients = []

        try:
            while True:
                try:
                    reads, writes, errs = select.select(rsocks, wsocks, [])
                except:
                    return
                for sock in reads:
                    if sock == self.socket:
                        client, address  = sock.accept()
                        print "Client ", address, " connected"
                        wsocks.append(client)
                        clients.append(client)
                    elif sock == self.inputsocket:
                        self.data.append(sock.recv(256))

                outstring = "" 
                if len(self.data) > 1:
                    msg_list = self.data[0].split('\n')
                    for msg in msg_list:
                        if msg != '':
                            curr_msg = msg.split(',')
                            process_message(curr_msg)
                            outstring = ','.join(curr_msg)
                            print curr_msg
                    self.data = []

                for sock in writes: 
                    if sock in clients:
                        if outstring != "":
                            sock.send(outstring)

        except KeyboardInterrupt:
            print "Server was stopped"
            self.inputsocket.close()
            self.socket.close()
            for sock in writes:
                sock.close()
            for sock in reads:
                sock.close()
            for sock in clients:
                sock.close()

process_message 这个函数只是添加了一些字段

一切运行得很好,但当客户端连接到服务器时,CPU的负载会飙升到100%。

这是客户端的代码:

import socket
mysocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
mysocket.connect(("127.0.0.1", 50001))
while True:
    data = mysocket.recv(512)
mysocket.close()
outfile.close()

这里是性能分析的统计数据:

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000   34.563   34.563 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 <string>:1(bind)
        1    0.000    0.000    0.000    0.000 <string>:1(connect)
  6870763    6.408    0.000    9.030    0.000 <string>:1(fileno)
        1    0.000    0.000    0.000    0.000 <string>:1(listen)
        1    0.000    0.000    0.000    0.000 server.py:113(__init__)
        1    4.409    4.409   34.556   34.556 server.py:123(start)
        1    0.007    0.007   34.563   34.563 server.py:176(main)
       91    0.002    0.000    0.010    0.000 server.py:80(process_message)
        3    0.000    0.000    0.000    0.000 socket.py:182(__init__)
        1    0.000    0.000    0.000    0.000 socket.py:196(accept)
       18    0.000    0.000    0.000    0.000 {getattr}
  2290358    0.906    0.000    0.906    0.000 {len}
        1    0.000    0.000    0.000    0.000 {method 'accept' of '_socket.socket' objects}
      186    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
        1    0.000    0.000    0.000    0.000 {method 'bind' of '_socket.socket' objects}
        1    0.000    0.000    0.000    0.000 {method 'connect' of '_socket.socket' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
  6870763    2.622    0.000    2.622    0.000 {method 'fileno' of '_socket.socket' objects}
      273    0.000    0.000    0.000    0.000 {method 'insert' of 'list' objects}
       91    0.000    0.000    0.000    0.000 {method 'join' of 'str' objects}
        1    0.000    0.000    0.000    0.000 {method 'listen' of '_socket.socket' objects}
      182    0.001    0.000    0.001    0.000 {method 'recv' of '_socket.socket' objects}
       71    0.001    0.000    0.001    0.000 {method 'send' of '_socket.socket' objects}
      182    0.000    0.000    0.000    0.000 {method 'split' of 'str' objects}
  2290268   20.197    0.000   29.227    0.000 {select.select}
       18    0.000    0.000    0.000    0.000 {setattr}

这到底是怎么回事呢?

1 个回答

1

我建议你理解为,select 函数会返回一个可写的套接字。如果没有可读的套接字,而输出字符串保持为空 '',那么你就会不停地重复这个循环,这样会导致 CPU 负担很重。所以,最好是只有在你确实需要写数据的时候,才把可写的套接字加入到列表中。这样,select 调用就会等到有数据可以读取的时候再继续。

关于你的评论,把:

reads, writes, errs = select.select(rsocks, wsocks, [])

替换为:

reads, writes, errs = select.select(rsocks, [], [])

并在 for sock in writes: 前面加上:

reads, writes, errs = select.select([], wsocks, [], 0)

撰写回答