Having 1-day experience in Twisted I try to schedule message sending in reply to tcp client:
import os, sys, time
from twisted.internet import protocol, reactor
self.scenario = [(1, "Message after 1 sec!"), (4, "This after 4 secs"), (2, "End final after 2 secs")]
for timeout, data in self.scenario:
reactor.callLater(timeout, self.sendata, data)
print "waited %d time, sent %s\n"%(timeout, data)
Now it sends messages, but I have 2 problems:
1) "timeout" is going from "now", and I want to count it after each previous task was completed (previous message was sent)
2) I don't know how to close connection after all messages were sent. If I place self.transport.loseConnection()
after callLater
s it closes connection immediately.
In previous try I didn't use reactor.callLater
, but only self.transport.write()
and time.sleep(n)
in for
loop. In this case all messages were sent together after all timeouts passed... Not something I wanted.
The purpose is to wait for client connection, wait timeout1 and send message1, wait timeout2 and send message2, ... etc. After final message - close connection.
Final solution for this deal..
The important thing to realize when working with Twisted is that nothing waits for anything. When you call
reactor.callLater()
, you're asking the reactor to call something later, not now. The call finishes right away (after the call has been scheduled, before it has been executed.) Consequently, yourprint
statement is a lie: you didn't waittimeout
time; you didn't wait at all.You can fix it in multiple ways, and which to use depends on what you actually want. If you want the second task to start four seconds after the first task started, you can simply add the delay (your
timeout
variable) of the first task to the delay of the second task. The first task may not start exactly when you schedule it, though; it may start later, if Twisted is too busy to start it sooner. Also, if your task takes a long time it may not actually be done before the second task starts.The more common way is for the first task to schedule the second task, instead of scheduling the second task right away. You can schedule it four seconds after the first task ended (by calling
reactor.callLater()
at the end of the first task), or four seconds after the first task started (by callingreactor.callLater()
at the start of the first task), or perform more complex calculations to determine when it should start, keeping track of elapsed time.When you realize nothing in Twisted waits, dealing with closing the connection when you've performed all scheduled tasks becomes easy: you simply have your last task call
self.transport.loseConnection()
. For more complex situations you may want to chainDeferred
s together, or use aDeferredList
to perform theloseConnection()
when all pending tasks have finished, even when they aren't strictly sequential.