Python TCP Server, запись для клиентов? - PullRequest
1 голос
/ 15 июня 2010

У меня есть 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 с соответствующим сокетом.Когда сокет доступен для записи, я бы затем вызвал соответствующую функцию записи клиентов.

Эта схема не выглядит очень элегантной, я хотел бы обработать запись в цикле основного сервера.Как мне это сделать?

Спасибо

Ответы [ 2 ]

1 голос
/ 16 июня 2010

Вот кое-что, что я написал некоторое время назад, чтобы узнать об обработке нескольких соединений в одном потоке. Это ни в коем случае не идеально, но иллюстрирует то, что вы хотите сделать. Объект client управляет потоками чтения и записи соединения и гарантирует, что на сервере есть клиентский сокет в правых списках select (). Это реализует простой протокол, где сообщения заканчиваются символами новой строки. Функции 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
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
0 голосов
/ 15 июня 2010

Реализация, которую вы описали в первом абзаце, звучит как обычный способ реализации сервера с использованием select.

Если вы хотите иметь возможность писать клиенту в «цикле основного сервера»,я предполагаю, что вы имеете в виду наличие кода, который выглядит примерно так:

request = socket.recv()
response = process_request(request)
socket.send(response)

, тогда вам потребуется отдельный поток для каждого клиента.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...