2010-07-21 7 views
6

Ayant une expérience de 1 jour dans Tordu essayer de programmer l'envoi de messages en réponse au client tcp:Python tordu: comment programmer?

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) 

Maintenant, il envoie des messages, mais j'ai 2 problèmes:
1) « délai d'attente » va de " maintenant ", et je veux le compter après que chaque tâche précédente soit terminée (le message précédent a été envoyé)
2) Je ne sais pas comment fermer la connexion après que tous les messages ont été envoyés. Si je place self.transport.loseConnection() après callLater il ferme la connexion immédiatement.

Essai précédent je ne pas utiliser reactor.callLater, mais seulement self.transport.write() et time.sleep(n) en boucle for. Dans ce cas, tous les messages ont été envoyés ensemble après tous les délais d'attente passés ... Pas quelque chose que je voulais.
Le but est d'attendre la connexion du client, d'attendre timeout1 et d'envoyer message1, d'attendre timeout2 et d'envoyer message2, ... etc. Après le message final - fermer la connexion.

Répondre

8

La chose importante à réaliser en travaillant avec Twisted est que rien n'attend rien. Lorsque vous appelez reactor.callLater(), vous demandez au réacteur d'appeler quelque chose plus tard, pas maintenant. L'appel se termine immédiatement (après que l'appel a été planifié, avant il a été exécuté.) Par conséquent, votre déclaration print est un mensonge: vous n'avez pas attendu timeout temps; tu n'as pas attendu du tout.

Vous pouvez le réparer de plusieurs façons, et celui à utiliser dépend de ce que vous voulez réellement. Si vous voulez que la deuxième tâche commence quatre secondes après que la première tâche a commencé, vous pouvez simplement ajouter le délai (votre variable timeout) de la première tâche au délai de la deuxième tâche. Cependant, la première tâche peut ne pas démarrer exactement lorsque vous la planifiez. il peut commencer plus tard, si Twisted est trop occupé pour le démarrer plus tôt. En outre, si votre tâche prend beaucoup de temps, il se peut qu'elle ne soit pas réellement effectuée avant le début de la deuxième tâche.

La méthode la plus courante consiste à planifier la deuxième tâche au lieu de planifier immédiatement la deuxième tâche. Vous pouvez le programmer quatre secondes après la fin de la première tâche (en appelant reactor.callLater() à la fin de la première tâche) ou quatre secondes après le début de la première tâche (en appelant reactor.callLater() au commencer de la première tâche) ou effectuer plus calculs complexes pour déterminer quand il devrait commencer, en gardant une trace du temps écoulé. Lorsque vous ne réalisez rien dans les attentes Twisted, la fermeture de la connexion lorsque vous avez effectué toutes les tâches planifiées devient facile: vous avez simplement votre dernier appel de tâche self.transport.loseConnection(). Pour les situations plus complexes, vous pouvez chaîner Deferred s ou utiliser un DeferredList pour exécuter le loseConnection() lorsque toutes les tâches en attente sont terminées, même si elles ne sont pas strictement séquentielles.

+0

Merci, maintenant je comprends pourquoi "sommeille" ne fonctionne pas. Pouvez-vous donner un exemple avec la planification de reactor.callLater() à la fin du précédent reactor.callLater()? – DominiCane

+0

Définissez simplement une fonction qui appelle 'self.sendata (data)', puis appelle 'reactor.callLater()' pour le rappel suivant, et passez cette fonction au premier 'reactor.callLater()' au lieu de 'self.sendata ' –

4

solution finale pour cette affaire ..

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

Juste fyi:' self.scenario .__ iter __() '->' iter (self.scenario) ',' self.sc.next() '->' next (self.sc) '(depuis 2.6 je pense) –

+0

Merci, l'a changé. – DominiCane