2009-03-03 14 views
5

Tengo una pregunta. Me gustaría enviar un flujo continuo de bytes a algún host durante cierto tiempo (digamos 1 minuto) usando Python.python: cómo enviar paquetes en múltiples hilos y luego el hilo se mata a sí mismo

Aquí está mi código hasta ahora:

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

Pocos cuestionan, ¿existe una mejor manera de dejar morir a un hilo después de 60 segundos que no sean de votación el time.Clock cada vez? Cuando ejecuto este programa, envía los bytes correctamente, pero cuando escribí "quit", el otro no morirá, aunque configuré la variable "killed" = True. Me pregunto, ¿por qué es así? el alcance de var Killed debe alcanzar el otro hilo ¿no?

Gracias

+0

¿Existe realmente un host en esa dirección? ¿Estás usando netcat para capturar la salida o algún otro programa? – johnny

+0

creo que el alcance de 'killed' está bien. – Jiri

Respuesta

0

Asegúrese de que el "dejar de fumar" está funcionando correctamente y añadir una letra pequeña para probar que la entrada está funcionando.

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

La variable transcurrida no se ha inicializado. Establézcalo a cero sobre el ciclo while.

2

No sé cómo hacer esto con el módulo "thread", pero puedo hacerlo con el módulo "threading". Creo que este código logra lo que quieres.

Para obtener documentación sobre el módulo 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 que utilizo time.time() en lugar de time.Clock(). time.clock() da el tiempo del procesador transcurrido en Unix (consulte http://docs.python.org/library/time.html). Creo que time.clock() debería funcionar en todas partes. Configuré test_time en 10 segundos porque no tengo paciencia durante un minuto.

Esto es lo que sucede si se deja correr los 10 segundos completos:

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

Esto es lo que sucede si escribo 'Salir':

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

Espero que esto ayude.

1

Como se mencionó anteriormente, use el módulo threading, es mucho más fácil de usar y proporciona varias primitivas de sincronización. También proporciona una clase Timer que se ejecuta después de un período de tiempo especificado.

Si solo desea que el programa salga, simplemente puede hacer que el hilo de envío sea un daemon. Para ello, llame a setDaemon (True) antes de llamar a start() (2.6 podría usar un atributo de daemon en su lugar). Python no se cerrará mientras se esté ejecutando un subproceso que no sea demonio.

5

He recomendado usar el módulo de roscado. Aún más beneficio es usar InterruptableThread para terminar el hilo. No es necesario utilizar el indicador para finalizar el hilo, pero se producirá una excepción si llama a terminate() en este hilo desde el elemento primario. Puede manejar excepciones 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: Puede volver a escribir el código como el uso de otro hilo que está esperando 1 minuto y luego matar a su otro hilo

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

Tenga en cuenta que para que este código funcione en sistemas de 64 bits (64 bits de Linux, de todos modos) debe ajustar el primer parámetro de 'PyThreadState_SetAsyncExc' en una llamada a' ctypes.c_long'. De lo contrario, se pasa como un entero de 32 bits, que se desborda, y obtienes una excepción ValueError "inexistente thread id". – intuited

0

Es fácil de probar el alcance 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

Puede hacerlo fácilmente sin hilos. Por ejemplo, el uso de trenzado, que acaba de configurar una llamada programada y un productor:

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

Esto tiene varias ventajas sobre el enfoque de rosca. No depende de los hilos daemon, por lo que puede limpiar la conexión de red (por ejemplo, para enviar un mensaje de cierre si es necesario) en lugar de depender de la plataforma para destruirlo. Maneja todo el código de red de bajo nivel real para usted (su ejemplo original es hacer lo incorrecto en el caso de socket.send que devuelve 0; este código manejará ese caso correctamente). Tampoco tiene que depender de ctypes o de la oscura API de CPython para generar una excepción en otro hilo (por lo que es portátil para más versiones de Python y puede interrumpir un envío bloqueado inmediatamente, a diferencia de algunos de los otros enfoques sugeridos).

Cuestiones relacionadas