开发者

python twisted - Timeouting on a sent message that did not get a response

开发者 https://www.devze.com 2023-02-14 08:03 出处:网络
I am creating a sort of a client-server implementation, and I\'d like to make sure that every sent message gets a response. So I want to create a timeout mechanism, which doesn\'t check if the message

I am creating a sort of a client-server implementation, and I'd like to make sure that every sent message gets a response. So I want to create a timeout mechanism, which doesn't check if the message itself is delivered, but rather checks if the delivered message gets a response.

IE, for two computers 1 and 2:

1: send successfully: "hello"
2: <<nothing>>
...
1: Didn't get a response for my "hello" -开发者_C百科-> timeout

I thought of doing it by creating a big boolean array with id for each message, which will hold a "in progress" flag, and will be set when the message's response is received.

I was wondering perhaps there was a better way of doing that.

Thanks, Ido.


There is a better way, which funnily enough I myself just implemented here. It uses the TimeoutMixin to achieve the timeout behaviour you need, and a DeferredLock to match up the correct replies with what was sent.

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"))

I haven't tested this, it's more of a starting point. There are a few things you might want to change here to suit your purposes:

  1. I use a LineOnlyReceiver — that's not relevant to the problem itself, and you'll need to replace sendLine/lineReceived with the appropriate API calls for your protocol.

  2. This is for a serial connection, so I don't deal with connectionLost etc. You might need to.

  3. I like to keep state directly in the instance. If you need extra state information, set it up in _doSend and clean it up in _cleanup. Some people don't like that — the alternative is to create nested functions inside _doSend that close over the state information that you need. You'll still need that self.deferred there though, otherwise lineReceived (or dataReceived) has no idea what to do.

How to use it

Like I said, I created this for serial communications, where I don't have to worry about factories, connectTCP, etc. If you're using TCP communications, you'll need to figure out the extra glue you need.

# 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)
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号