python twisted - 发送消息无回应时超时

3 投票
1 回答
2082 浏览
提问于 2025-04-16 12:39

我正在做一个客户端和服务器之间的通信实现,想确保每条发送的消息都有回复。所以我想创建一个超时机制,这个机制不是检查消息是否送达,而是检查送达的消息是否得到了回复。

比如,有两台电脑,1号和2号:

1: send successfully: "hello"
2: <<nothing>>
...
1: Didn't get a response for my "hello" --> timeout

我想通过创建一个大的布尔数组来实现,每条消息都有一个唯一的标识符,这个数组会保存一个“进行中”的标记,当收到消息的回复时,这个标记就会被设置。

我在想,或许还有更好的方法来实现这个功能。

谢谢,
Ido。

1 个回答

5

有一种更好的方法,搞笑的是我自己刚刚在这里实现了这个。它使用了TimeoutMixin来实现你需要的超时功能,并且用DeferredLock来匹配正确的回复和发送的内容。

from twisted.internet import defer
from twisted.protocols.policies import TimeoutMixin
from twisted.protocols.basic import LineOnlyReceiver

class PingPongProtocol(LineOnlyReceiver, TimeoutMixin):

    def __init__(self):
        self.lock = defer.DeferredLock()
        self.deferred = None

    def sendMessage(self, msg):
        result = self.lock.run(self._doSend, msg)
        return result

    def _doSend(self, msg):
        assert self.deferred is None, "Already waiting for reply!"

        self.deferred = defer.Deferred()
        self.deferred.addBoth(self._cleanup)
        self.setTimeout(self.DEFAULT_TIMEOUT)
        self.sendLine(msg)
        return self.deferred

    def _cleanup(self, res):
        self.deferred = None
        return res

    def lineReceived(self, line):
        if self.deferred:
            self.setTimeout(None)
            self.deferred.callback(line)
        # If not, we've timed out or this is a spurious line

    def timeoutConnection(self):
        self.deferred.errback(
            Timeout("Some informative message"))

我还没有测试这个,算是一个起点。这里有几个地方你可能想根据自己的需求进行修改:

  1. 我使用了LineOnlyReceiver——这和问题本身没有关系,你需要用适合你协议的API调用来替换sendLine/lineReceived

  2. 这是用于串行连接的,所以我没有处理connectionLost等情况。你可能需要处理这些。

  3. 我喜欢直接在实例中保持状态。如果你需要额外的状态信息,可以在_doSend中设置,并在_cleanup中清理。有些人不喜欢这样——另一种方法是在_doSend内部创建嵌套函数,来处理你需要的状态信息。不过你仍然需要那个self.deferred,否则lineReceived(或者dataReceived)就不知道该怎么做了。

如何使用它

正如我所说,我是为串行通信创建这个的,所以我不需要担心工厂、connectTCP等。如果你使用的是TCP通信,你需要弄清楚需要额外的哪些配合。

# Create the protocol somehow. Maybe this actually happens in a factory,
# in which case, the factory could have wrapper methods for this.
protocol = PingPongProtocol()
def = protocol.sendMessage("Hi there!")
def.addCallbacks(gotHiResponse, noHiResponse)

撰写回答