2009-03-03 7 views
5

J'ai une question. Je voudrais envoyer un flux continu d'octets à un hôte pendant un certain temps (disons 1 minute) en utilisant python.python: comment envoyer des paquets dans un multi-thread, puis le fil se tue

Voici mon code à ce jour:

#! /usr/bin/env python               

import socket 
import thread 
import time 

IP = "192.168.0.2" 
PADDING = "a" * 1000 #assume the MTU is slighly above 1000 
DATA = PADDING + "this is sentence number = " 
PORT = 14444 
killed = False 
test_time = 60 #60 seconds of testing 

def send_data(): 
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
    s.connect((IP, PORT)) 
    count = 1 
    starttime = time.clock() 
    while elapsed < test_time: 
    sent = s.send(DATA + str(count) + "\n") 
    if sent == 0: break # assume that if nothing is sent -> connection died 
    count = count+1 
    elapsed = time.clock() - starttime 
    if killed: 
     break 
    s.close() 
    print str(count) + " has been sent" 

print "to quit type quit" 
thread.start_new_thread(send_data,()) 

while True: 
    var = raw_input("Enter something: ") 
    if var == "quit": 
    killed = True 

Peu question, est-il une meilleure façon de laisser mourir de fil au bout de 60 secondes autres que le vote time.clock chaque fois? Quand j'exécute ce programme, il envoie les octets correctement, mais quand je tape, l'autre thread ne meurt pas, même si je mets la var kill = True. Je me demande pourquoi est-ce? la portée de la var Killed devrait atteindre l'autre fil droit?

Merci

+0

Un hôte existe-t-il réellement à cette adresse? Utilisez-vous Netcat pour capturer la sortie ou un autre programme? – johnny

+0

Je pense que la portée de 'tué' est ok. – Jiri

Répondre

0

Assurez-vous que le « quitter » fonctionne correctement et ajouter une petite impression pour vérifier que l'entrée fonctionne.

if var == "quit": 
print "Hey we got quit" 
0

La variable écoulée n'est pas initialisée. Réglez-le à zéro au-dessus de la boucle while.

2

Je ne sais pas comment faire cela avec le module "thread", mais je peux le faire avec le module "threading". Je pense que ce code accomplit ce que vous voulez.

Pour la documentation sur le module de filetage: http://docs.python.org/library/threading.html

#!/usr/bin/python 

import time 
from threading import Thread 
import threading 
import sys 

test_time = 10 
killed = False 

class SillyThread(threading.Thread): 
    def run(self): 
     global killed 
     starttime = time.time() 
     counter = 0 
     while (time.time() - starttime) < test_time: 
      if killed: 
       break 
      counter = counter + 1 
      time.sleep(0.1) 
     print "I did %d loops" % counter 

class ManageThread(threading.Thread): 
    def run(self): 
     global killed 
     while True: 
      var = raw_input("Enter something: ") 
      if var == "quit": 
       killed = True 
       break 
     print "Got var [%s]" % var 

silly = SillyThread() 
silly.start() 
ManageThread().start() 
Thread.join(silly) 
print "bye bye" 
sys.exit(0) 

Notez que j'utilise time.time() au lieu de time.clock(). time.clock() donne le temps processeur écoulé sous Unix (voir http://docs.python.org/library/time.html). Je pense que time.clock() devrait fonctionner partout. J'ai mis mon test_time à 10 secondes parce que je n'ai pas la patience pour une minute.

Voilà ce qui se passe si je laisse courir les pleins 10 secondes:

[email protected]:~/tmp$ ./test.py 
Enter something: I did 100 loops 
bye bye 

Voilà ce qui se passe si je tape 'quitter':

[email protected]:~/tmp$ ./test.py 
Enter something: quit 
Got var [quit] 
I did 10 loops 
bye bye 

Hope this helps.

1

Comme mentionné ci-dessus, utilisez le module threading, il est beaucoup plus facile à utiliser et fournit plusieurs primitives de synchronisation. Il fournit également une classe Timer qui s'exécute après un certain temps.

Si vous voulez simplement que le programme se termine, vous pouvez simplement transformer le thread expéditeur en démon. Vous faites cela en appelant setDaemon (True) avant d'appeler start() (2.6 pourrait utiliser un attribut daemon à la place). Python ne quittera pas tant qu'un thread non démon est en cours d'exécution.

5

Je recommned utilisant le module d'enfilage. Encore plus d'avantages est d'utiliser InterruptableThread pour terminer le thread. Vous n'avez pas besoin d'utiliser flag pour terminer votre thread mais une exception se produira si vous appelez terminate() sur ce thread depuis le parent. Vous pouvez gérer l'exception ou non.

import threading, ctypes 

class InterruptableThread(threading.Thread): 
@classmethod 
def _async_raise(cls, tid, excobj): 
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(excobj)) 
    if res == 0: 
     raise ValueError("nonexistent thread id") 
    elif res > 1: 
     ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0) 
     raise SystemError("PyThreadState_SetAsyncExc failed") 

def raise_exc(self, excobj): 
    assert self.isAlive(), "thread must be started" 
    for tid, tobj in threading._active.items(): 
     if tobj is self: 
      self._async_raise(tid, excobj) 
      return 

def terminate(self): 
    self.raise_exc(SystemExit) 

EDIT: Vous pouvez réécrire votre code comme ceci en utilisant un autre thread qui attend 1 minute, puis tuer votre autre thread

def send_data: 
    IP = ... 
    # other vars 

    ... 
    s = socket.socket(.....) 

    # no killed checking 
    # no time checking 
    # just do your work here 
    ... 
    s.close() 


my_thread = InterruptableThread(target=send_data) 
my_thread.start() 

def one_minute_kill(who): 
    time.sleep(60) 
    who.terminate() 

killer_thread = InterruptableThread(target=one_minute_kill, args=[my_thread]) 
killer.start() 

print "to quit type quit" 
while my_thread.isAlive(): 
    if raw_input("Enter something: ") == "quit": 
    my_thread.terminate() 
+1

Notez que pour que ce code fonctionne sous des systèmes 64 bits (linux 64 bits, de toute façon), vous devez enrouler le premier paramètre de 'PyThreadState_SetAsyncExc' dans un appel à' ctypes.c_long'. Sinon, il est passé en entier de 32 bits, ce qui déborde, et vous obtenez une exception ValueError "id thread inexistant". – intuited

0

Il est facile de tester la portée de killed:

>>> import thread 
>>> killed = False 
>>> import time 
>>> def test(): 
... while True: 
... time.sleep(1) 
... if killed: 
...  print 'Dead.' 
...  break 
... 
>>> thread.start_new_thread(test,()) 
25479680 
>>> time.sleep(3) 
>>> killed = True 
>>> Dead. 
1

Vous pouvez le faire assez facilement sans threads. Par exemple, en utilisant Twisted, vous venez de mettre en place un appel chronométré et un producteur:

from twisted.internet.protocol import ClientFactory, Protocol 
from twisted.internet import reactor 

class Noisy(Protocol): 
    def __init__(self, delay, data): 
     self.delay = delay 
     self.data = data 

    def stop(self): 
     self.transport.unregisterProducer() 
     self.transport.loseConnection() 
     reactor.stop() 

    def resumeProducing(self): 
     self.transport.write(self.data) 

    def connectionMade(self): 
     self.transport.registerProducer(self, False) 
     reactor.callLater(self.delay, self.stop) 

factory = ClientFactory() 
factory.protocol = lambda: Noisy(60, "hello server") 
reactor.connectTCP(host, port, factory) 
reactor.run() 

Cela a plusieurs avantages par rapport à l'approche filetée. Il ne repose pas sur les threads démon, vous pouvez donc nettoyer la connexion réseau (par exemple, envoyer un message de fermeture si nécessaire) plutôt que de dépendre de la plate-forme pour le détruire. Il gère pour vous tout le code de réseau bas niveau actuel (votre exemple original ne fait pas l'affaire dans le cas de socket.send retournant 0, ce code traitera correctement ce cas). Vous n'avez pas non plus besoin de ctypes ou de l'obscure API CPython pour déclencher une exception dans un autre thread (il est donc portable sur plusieurs versions de Python et peut interrompre un envoi bloqué immédiatement, contrairement à d'autres approches).

Questions connexes