2009-03-03 14 views
5

Ho una domanda. Mi piacerebbe inviare flussi continui di byte a qualche host per un certo periodo di tempo (diciamo 1 minuto) usando python.python: come inviare pacchetti in multi-thread e poi il thread si uccide da solo

Ecco il mio codice finora:

#! /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 

Pochi domanda, c'è un modo migliore per lasciare un dado filo dopo 60 secondi diversi polling del time.clock ogni volta? Quando eseguo questo programma, invia correttamente i byte ma quando ho digitato quit l'altro thread non morirà, anche se imposto la variabile killed = True. Mi chiedo perché è così? l'ambito di var Killed dovrebbe raggiungere l'altro thread giusto?

Grazie

+0

Un host esiste effettivamente a quell'indirizzo? Stai usando netcat per catturare l'output o qualche altro programma? – johnny

+0

penso che l'ambito di "ucciso" sia ok. – Jiri

risposta

0

garantire che il "quit" sta funzionando correttamente e aggiungere una piccola stampa per verificare che l'ingresso è in funzione.

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

La variabile scaduta non è inizializzata. Impostalo su zero sopra il ciclo while.

2

Non so come farlo con il modulo "thread", ma posso farlo con il modulo "threading". Penso che questo codice realizzi ciò che vuoi.

Per la documentazione sul modulo threading: 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) 

Nota che uso time.time() al posto di time.clock(). time.clock() indica il tempo trascorso del processore su Unix (vedere http://docs.python.org/library/time.html). Penso che time.clock() dovrebbe funzionare ovunque. Ho impostato il mio test_time a 10 secondi perché non ho la pazienza per un minuto.

Ecco cosa succede se ho lasciato correre i ben 10 secondi:

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

Ecco cosa succede se digito 'quit':

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

Spero che questo aiuti.

1

Come accennato in precedenza, utilizzare il modulo threading, è molto più facile da usare e fornisce diverse primitive di sincronizzazione. Fornisce anche una classe Timer che viene eseguita dopo un determinato periodo di tempo.

Se si desidera solo il programma per uscire, si può semplicemente fare il filo invio di un demone. A tale scopo, è necessario impostare setDaemon (True) prima di chiamare start() (2.6 potrebbe invece utilizzare un attributo demone). Python non uscirà fintanto che un thread non daemon è in esecuzione.

5

mi raccomando utilizzando il modulo threading. Un altro vantaggio è l'utilizzo di InterruptableThread per terminare il thread. Non è necessario usare la bandiera per la terminazione tuo thread, ma un'eccezione si verifica se si chiama terminate() su questo thread da genitore. Puoi gestire l'eccezione o no.

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: È possibile riscrivere il codice come questo utilizzando un altro thread in attesa 1 minuto e poi uccidere il vostro altro 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

Si noti che per fare in modo che questo codice funzioni con sistemi a 64 bit (linux a 64 bit, comunque) è necessario avvolgere il primo parametro di 'PyThreadState_SetAsyncExc' in una chiamata a' ctypes.c_long'. Altrimenti viene passato come numero intero a 32 bit, che trabocca, e si ottiene un'eccezione ValueError "inesistente ID thread". – intuited

0

E 'facile per testare il campo di applicazione 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

Si può fare abbastanza facilmente senza fili. Ad esempio, utilizzando Contorto, basta impostare una chiamata a tempo e un produttore:

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

Questo deve vari vantaggi rispetto all'approccio filettato. Non si basa su thread daemon, quindi puoi effettivamente ripulire la connessione di rete (ad esempio, per inviare un messaggio di chiusura se necessario) piuttosto che affidarti alla piattaforma per distruggerlo. Gestisce tutto il codice di rete di basso livello per te (il tuo esempio originale sta facendo la cosa sbagliata nel caso di socket.send che restituisce 0, questo codice gestirà correttamente tale caso). Inoltre non devi fare affidamento sui ctype o sull'oscura API CPython per sollevare un'eccezione in un altro thread (quindi è portabile a più versioni di Python e può effettivamente interrompere immediatamente un invio bloccato, diversamente da altri approcci suggeriti).