如何通过工厂使用Twisted协议发送数据

7 投票
1 回答
6236 浏览
提问于 2025-04-16 18:08

我正在写一个客户端,目的是实现一个自定义的通信协议,并且我有一个工厂来处理这个协议。我的问题是这样的:我的客户端需要双向通信,有时候我想告诉它“发送这些数据”。但是我手里只有工厂对象:

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 中启动其他需要使用连接协议的操作,这样它们就不会在连接实际可用之前就尝试使用它。

撰写回答