如何通过工厂使用Twisted协议发送数据
我正在写一个客户端,目的是实现一个自定义的通信协议,并且我有一个工厂来处理这个协议。我的问题是这样的:我的客户端需要双向通信,有时候我想告诉它“发送这些数据”。但是我手里只有工厂对象:
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 个回答
10
你可以访问你想要的所有对象。工厂负责创建协议实例,所以如果你想让协议实例在工厂可以使用的地方保留,就重写 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)
你还可以在 cbConnected
回调中保存对 connectedProtocol
的引用,这样你就可以在之后继续使用它。你也可以在 cbConnected
中启动其他需要使用连接协议的操作,这样它们就不会在连接实际可用之前就尝试使用它。