2010-07-21 3 views
6

Имея 1-дневный опыт в Twisted я стараюсь запланировать отправку сообщений в ответ на TCP клиента:Python скрученный: как планировать?

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) 

Теперь он отправляет сообщения, но у меня есть 2 проблемы:
1) «Тайм-аут» собирается из " теперь », и я хочу считать это после завершения каждой предыдущей задачи (отправлено предыдущее сообщение)
2) Я не знаю, как закрыть соединение после отправки всех сообщений. Если я помещаю self.transport.loseConnection() после callLater, он немедленно закрывает соединение.

В предыдущей попытке я не использовал reactor.callLater, но только self.transport.write() и time.sleep(n) в for цикл. В этом случае все сообщения были отправлены вместе после прохождения всех тайм-аутов ... Не то, что я хотел.
Цель состоит в том, чтобы дождаться соединения с клиентом, ждать timeout1 и отправлять сообщение1, ждать timeout2 и отправлять сообщение2, ... и т. Д. После окончательного сообщения - закрыть соединение.

ответ

8

Важная вещь, которую нужно реализовать при работе с Twisted, заключается в том, что ничего не ждет ничего. Когда вы звоните reactor.callLater(), вы просите реактор позвонить что-то позже, а не сейчас. Вызов завершается сразу же после того, как звонок был запланирован, до был выполнен.) Следовательно, ваш оператор print - ложь: вы не ожидали timeout раз; вы не ожидали.

Вы можете исправить это несколькими способами, и использовать их зависит от того, что вы на самом деле хотите. Если вы хотите, чтобы вторая задача начиналась через четыре секунды после первой задачи началось, вы можете просто добавить задержку (свою timeout переменную) первой задачи к задержке второй задачи. Однако первая задача может не начинаться точно, когда вы планируете ее; он может начаться позже, если Twisted слишком занят, чтобы начать его раньше. Кроме того, если ваша задача занимает много времени, она может не выполняться до начала второй задачи.

Наиболее распространенный способ для первой задачи - запланировать вторую задачу, а не планировать вторую задачу сразу. Вы можете запланировать его через четыре секунды после завершения первой задачи (путем вызова reactor.callLater() в конце первой задачи) или через четыре секунды после запуска первой задачи (путем вызова reactor.callLater() по началу в начале первой задачи) или выполнить больше сложные вычисления, чтобы определить, когда он должен начаться, отслеживая прошедшее время.

Когда вы ничего не понимаете в ожиданиях Twisted, закрытие соединения, когда вы выполняете все запланированные задачи, становится простым: вы просто имеете свой последний вызов задачи self.transport.loseConnection(). Для более сложных ситуаций вам может понадобиться связать Deferred s или использовать DeferredList для выполнения loseConnection(), когда все незавершенные задачи завершены, даже если они не являются строго последовательными.

+0

Спасибо, теперь я понимаю, почему «спит» не работает. Можете ли вы привести пример с расписанием реактор.callLater() в конце предыдущего реактора.callLater()? – DominiCane

+0

Просто определите функцию, которая вызывает 'self.sendata (data)', а затем вызывает 'reactor.callLater()' для следующего обратного вызова и передаёт эту функцию в первый 'reactor.callLater()' вместо 'self.sendata ' –

4

Окончательное решение этой сделки ..

import os, sys, time 
from twisted.internet import protocol, reactor 
import itertools 

def sendScenario(self): 
    def sendelayed(d): 
     self.sendata(d) 
     self.factory.out_dump.write(d) 
     try: 
      timeout, data = next(self.sc) 
      reactor.callLater(timeout, sendelayed, data) 
     except StopIteration: 
      print "Scenario completed!" 
      self.transport.loseConnection() 

    self.scenario = [(1, "Message after 1 sec!"), (4, "This after 4 secs"), (2, "End final after 2 secs")] 
    self.sc = iter(self.scenario) 
    timeout, data = next(self.sc) 
    reactor.callLater(timeout, sendelayed, data) 
+0

Просто fyi:' self.scenario .__ iter __() '->' iter (self.scenario) ',' self.sc.next() '->' next (self.sc) '(начиная с 2.6, я думаю) –

+0

Спасибо, изменил его. – DominiCane

Смежные вопросы