Как отправить данные по витому протоколу через фабрику

Я пишу клиент, реализующий собственный протокол, и у меня есть фабрика для него. Моя проблема заключается в следующем: мой клиент имеет двухстороннюю связь, и иногда я хочу сказать ему "отправить эти данные". Но все, что у меня есть, это заводской объект:

class MyFactory(ClientFactory):
    protocol = MyProtocol

    def __init__(self, recv_callback):
        self.recv_callback = recv_callback

    def send_message(self, msg):
        self.protocol.send_message(msg)

Итак, я создаю фабрику и имею фабричный объект, а не объект протокола. когда send_message выше называется я получаю ошибку, потому что self.protocol это просто класс, а не объект.

Как я могу это сделать? Должен ли я также выставить протокол для подключения в дополнение к заводским?

Спасибо

1 ответ

Решение

У вас есть доступ ко всем объектам, которые вы хотите. Фабрика отвечает за создание экземпляров протокола, поэтому, если вы хотите сохранить экземпляр протокола там, где фабрика может его использовать, переопределите buildProtocol и сохраните экземпляр:

class MyFactory(ClientFactory):
    protocol = MyProtocol

    ...

    def buildProtocol(self, address):
        proto = ClientFactory.buildProtocol(self, address)
        self.connectedProtocol = proto
        return proto

Однако у этого подхода отсутствует одна важная особенность. Это не позволяет легко сказать, когда buildProtocol был вызван и connectedProtocol был установлен. Если вы попытаетесь использовать этот атрибут наивно:

factory = MyFactory()
reactor.connectTCP(host, port, factory)
factory.connectedProtocol.send_message(...)

Код не удастся с AttributeError потому что соединение еще не было установлено. Поскольку Twisted управляется событиями, вам нужно обязательно использовать этот код, отвечая на событие, которое говорит о том, что соединение установлено.

Вы можете сделать это, запустив функцию обратного вызова при создании протокола, а не просто устанавливая атрибут. У Twisted есть вспомогательная фабрика, которая уже делает что-то вроде этого:

from twisted.internet.protocol import ClientCreator

cc = ClientCreator(reactor, MyProtocol)
whenConnected = cc.connectTCP(host, port)

# Or the equivalent with endpoints
#  from twisted.internet.endpoints import TCP4ClientEndpoint
#  from twisted.internet.protocol import ClientFactory
#  endpoint = TCP4ClientEndpoint(reactor, host, port)
#  factory = ClientFactory()
#  factory.protocol = MyProtocol
#  whenConnected = endpoint.connect(factory)

def cbConnected(connectedProtocol):
    connectedProtocol.send_message(...)

def ebConnectError(reason):
    # Connection attempt failed, perhaps retry
    ...

whenConnected.addCallbacks(cbConnected, ebConnectError)

Вы также можете сохранить ссылку на connectedProtocol в cbConnected обратный вызов, чтобы вы могли продолжать использовать его позже. Вы также можете запустить любые другие операции, которые захотят использовать подключенный протокол в cbConnectedчтобы они не пытались использовать соединение до того, как оно действительно станет доступным.

Другие вопросы по тегам