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