我有一台tcp服务器,它使用select调用从客户端多路读取。
我有一个客户端类(MClient),用于管理传入数据包的解码

while(1)

    rlist, wlist, xlist = select( input_sockets, output_sockets, [] , 1)

    for insock in rlist: #any clients????

        if insock is server_socket:
            new_socket, addr = server_socket.accept()
            input_sockets.append(new_socket)
            clients[insock.fileno()] = MClient(new_socket, addr, client_id) #dict of clients
        else:
            data = insock.recv(512)
            if data:
                clients[insock.fileno()].ProcessPacket(data)
            else:
                input_sockets.remove(insock)
                del clients[insock.fileno()]

    #handle writing to sockets
    for outsock in wlist:
        ....not done yet



   #do some other stuff not associated with the socket

我对如何处理将数据发送回客户端的困惑,即如何写入列表'output_sockets'。我正在考虑在我的MClient对象中设置一个标志,该标志指示我有要发送回客户端的数据。然后,我将在服务器循环中检查每个客户端,以查看是否设置了此标志,然后使用相应的套接字填充output_list。当套接字可用于写操作时,我将调用适当的客户端写函数。

这个方案看起来不是很优雅,我想在主服务器循环中处理编写。我将如何完成?

谢谢

最佳答案

这是我前一段时间写的,以了解有关使用单个线程处理多个连接的信息。它绝不是完美的,但可以说明您想要做什么。客户端对象管理连接的读写流,并确保服务器在正确的select()列表中具有客户端套接字。这实现了一个简单的协议(protocol),其中消息以换行符终止。 pumpXXXX()函数仅阻止读取/写入流并管理读取/写入缓冲区。仅当在缓冲区中找到换行符时,才处理完整消息。

import socket
import select

class Client(object):

    '''This object is created for each client connection.  It tracks
    what has been read, what has been written, and processes complete
    messages terminated by newlines.  It responds by returning the
    original message wrapped in square brackets and terminated by a
    newline. '''

    def __init__(self,who,sock,server):

        '''who - client address
        sock - client socket
        server - server object for this client
        '''

        self.who = who
        self.readbuf = ''
        self.writbuf = ''
        self.server = server
        self.sock = sock

    def close(self):

        '''Removes client from server's reader/writer queues and
        closes the connection.'''

        self.sock.close()
        if self.sock in self.server.readers:
            self.server.readers.remove(self.sock)
        if self.sock in self.server.writers:
            self.server.writers.remove(self.sock)
        self.server.data.pop(self.sock)

    def pumprecv(self):

        '''Server calls pumprecv() when something is readable from the
        client socket.  The data is appended to the client's read
        buffer.mro Complete messages (if any) are then removed from
        the buffer and processed.'''

        try:
            tmp = self.sock.recv(1000)
        except socket.error,e:
            print 'recv',e
            self.close()
        else:
            if tmp:
                self.readbuf += tmp

                # Complete messages are processed
                while '\n' in self.readbuf:
                    msg,self.readbuf = self.readbuf.split('\n',1)
                    print self.who,msg
                    self.writbuf += '[' + msg + ']\n'
                    # New data to send.  Make sure client is in the
                    # server's writer queue.
                    if self.sock not in self.server.writers:
                        self.server.writers.append(self.sock)
            else:
                self.close()

    def pumpsend(self):
        try:
            # send some data.  tmp is #chars sent (may not be all in writbuf).
            tmp = self.sock.send(self.writbuf)
        except socket.error,e:
            print 'send:',e
            self.close()
        else:
            # Removed sent characters from writbuf.
            self.writbuf = self.writbuf[tmp:]
            # If writbuf is empty, remove socket from server's write queue.
            if not self.writbuf:
                self.server.writers.remove(self.sock)

class Server(object):
    def __init__(self,ip='127.0.0.1',port=9999):
        self.ssock = socket.socket()
        self.ssock.bind((ip,port))
        self.ssock.listen(5)
        self.readers = [self.ssock]
        self.data = {}
        self.writers = []
        self.quit = False

    def pumpaccept(self):

        '''Called when server socket is readable to accept a
        connection and create a Client object.'''

        csock,who = self.ssock.accept()
        print 'Connected %s:%d' % who
        self.readers.append(csock)
        self.data[csock] = Client(who,csock,self)

    def serve(self):
        while not self.quit or self.writers:
            readable,writable,other = select.select(self.readers,self.writers,[],1.0)
            # Operate on copies of the queues since the pumpXXX() commands can modify the lists.
            if self.ssock in readable[:]:
                self.pumpaccept()
                readable.remove(self.ssock)
            for reader in readable[:]:
                self.data[reader].pumprecv()
            for writer in writable[:]:
                self.data[writer].pumpsend()

            if not readable and not writable and not other:
                print '.',

if __name__ == '__main__':
    srv = Server()
    srv.serve()

我通过在一个控制台中启动服务器,然后在其他控制台中运行以下代码来测试多个连接来进行测试。建立多个连接,从不同的窗口交替发送,并发送部分消息以查看服务器如何响应。
>>> from socket import *
>>> s=socket()
>>> s.connect(('localhost',9999))
>>> s.send('one\ntwo\nthree')
13
>>> s.send('\nfour\n')
6
>>> s.recv(1024)
'[one]\n[two\three]\n[four]\n'
>>> s.close()

输出应如下所示:
. . . . . . . . . . . . . . . . . . . Connected 127.0.0.1:1514
. . . . . . . . . ('127.0.0.1', 1514) one
. . . . . . . ('127.0.0.1', 1514) two
. . . ('127.0.0.1', 1514) three
('127.0.0.1', 1514) four
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

10-08 06:51
查看更多