Сервер Python asyncore отправляет данные только в один носок

Я должен отправлять данные только на соединение, что я могу сделать?

сервер:

import asyncore, socket, threading

class EchoHandler(asyncore.dispatcher_with_send):
    def __init__(self,sock):
        asyncore.dispatcher.__init__(self,sock=sock);
        self.out_buffer = ''

    def handle_read(self):
        datos = self.recv(1024);
        if datos:
            print(datos);
            self.sock[0].send("signal");

class Server(asyncore.dispatcher):

    def __init__(self,host='',port=6666):
        asyncore.dispatcher.__init__(self);
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM);
        self.set_reuse_addr();
        self.bind((host,port));
        self.listen(1);

    def handle_accept(self):
        self.sock,self.addr = self.accept();
        if self.addr:
            print self.addr[0];
        handler = EchoHandler(self.sock);

    def handle_close(self):
        self.close();     

cliente = Server();
asyncore.loop()

эта строка пример не работает, но я хочу отправить данные в нулевой носок:

self.sock[0].send("probando");

например, если у меня 5 сокетов, выбираю, кому отправлять данные


person Francisco    schedule 30.07.2013    source источник


Ответы (1)


Объяснение

Вы пытались получить sock из списка и выполнить его метод отправки. Это вызывает ошибку, потому что EchoHandler не имеет ни атрибута sock, ни списка сокетов. Правильный метод - получить экземпляр EchoHandler, который вы хотите (на основе, например, IP-адреса или слотов, назначенных некоторым пользовательским протоколом), а затем использовать его метод отправки - здесь (с dispatcher_with_send) также лучше использовать специальный буфер для чем отправить.

EchoHandler экземпляр создается при каждом приеме соединения - с этого момента он является установленным каналом для связи с данным хостом. Server прослушивает любое неустановленное соединение, в то время как EchoHandler использует socks (предоставленные Server в handle_accept) для установленных соединений, поэтому существует столько экземпляров EchoHandler, сколько соединений.

Решение

Вам нужно составить список соединений (EchoHandler экземпляров; мы будем использовать буфер, а не send() сокета напрямую) и дать им возможность удалять свои записи при закрытии:

class Server(asyncore.dispatcher):
    def __init__(self, host='', port=6666):
        ...
        self.connections = []

    def handle_accept(self):
        ...
        handler = EchoHandler(self.sock, self);
        self.connections.append(self.sock)

    ...

    def remove_channel(self, sock):
        if sock in self.connections:
            self.connections.remove(sock)

class EchoHandler(asyncore.dispatcher_with_send):
    def __init__(self, sock, server):
        ...
        self.server = server

    def handle_read(self):
        datos = self.recv(1024);
        if datos:
            print(datos);
            self.out_buffer += 'I echo you: ' + datos

    def handle_close(self):
        self.server.remove_channel(self)
        self.close()

EchoHandler теперь знает об экземпляре сервера и может удалить его сокет из списка. Этот эхо-пример теперь полностью функционален, и с рабочим списком сокетов мы можем перейти к асинхронной отправке.

Но на этом этапе вы можете использовать этот список по своему усмотрению - cliente.connections[0].out_buffer += 'I am data' сделает всю работу, но, вероятно, вам нужно лучше контролировать это. Если да, то вперед.

«Для кого, мной»

Чтобы отправлять данные асинхронно, нам нужно отделить asyncore от нашего потока управления, в котором мы будем вводить, что и кому отправлять.

class ServerThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.daemon = True # if thread is a daemon, it'll be killed when main program exits
        self.cliente = Server()
        self.start()

    def run(self):
        print 'Starting server thread...'
        asyncore.loop()

thread = ServerThread()

while True:
    msg = raw_input('Enter IP and message divided by semicolon: ')

    if msg == 'exit':
        break

    ip, data = msg.split('; ')
    for sock in thread.cliente.connections:
        if sock.addr[0] == ip:
            sock.out_buffer += data
            break

Это будет работать и ждать IP-адреса и данных назначения. Не забудьте подключить клиент.

Как я уже сказал, вы можете использовать что угодно, чтобы указать, какой сокет какой. Это может быть класс с полями, например. IP и имя пользователя, чтобы вы могли отправлять данные только пирам, чьи имена пользователей начинаются с «D».

Но...

Это решение немного грубое и требует лучшего знания модуля asyncore, если вы хотите красиво отправлять данные (здесь есть некоторая задержка из-за того, как работает select()) и хорошо использовать эту оболочку socket. здесь и здесь приведены некоторые ресурсы.

Примечание по синтаксису

Хотя ваш код теперь будет работать, в нем есть некоторые неприятные вещи. Точки с запятой в конце инструкций не вызывают ошибок, но создание почти каждой переменной атрибута класса может привести к ним. Например здесь:

def handle_accept(self):
    self.sock,self.addr = self.accept();
    if self.addr:
        print self.addr[0];
    handler = EchoHandler(self.sock);

self.sock и self.addr могут использоваться в этом классе для чего-то другого (например, для вещей, связанных с сокетами; адресов), и их переопределение может создать проблемы. Методы, используемые для запросов, никогда не должны сохранять состояние предыдущих действий.

Я надеюсь, что Python будет достаточно хорош для того, чтобы вы продолжали его использовать!

Редактировать: sock.addr[0] можно использовать вместо sock.socket.getpeername()[0], но для этого требуется, чтобы self.addr не изменялось, поэтому handle_accept() должно выглядеть так:

def handle_accept(self):
    sock, addr = self.accept()
    if addr:
        print addr[0]
    handler = EchoHandler(sock, self)
    self.connections.append(handler)
person Daniel Sawka    schedule 02.08.2013