2009-03-03 11 views
5

Mam pytanie. Chciałbym wysłać ciągłe strumienie bajtów do jakiegoś hosta przez pewien czas (powiedzmy 1 minutę) używając Pythona.python: jak wysłać pakiety w wielu wątkach, a następnie sam zabić wątek

Oto mój kod do tej pory:

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

Niewiele pytanie, czy istnieje lepszy sposób, aby pozwolić kostką gwintu po 60 sekundach innych niż wywoływania time.clock za każdym razem? Gdy uruchomię ten program, wysyła on bajty poprawnie, ale kiedy wypiszę klawisz, drugi wątek nie zginie, nawet jeśli ustawiłem var zabity = True. Zastanawiam się, dlaczego tak jest? zakres var Killed powinien dotrzeć do drugiego wątku w prawo?

Dzięki

+0

Czy host rzeczywiście istnieje pod tym adresem? Czy używasz Netcat do przechwytywania danych wyjściowych lub jakiegoś innego programu? – johnny

+0

Myślę, że zakres "zabitych" jest w porządku. – Jiri

Odpowiedz

0

Upewnić się, że „quit” działa poprawnie i dodać niewielką drukiem, aby sprawdzić, czy wejście działa.

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

Zmienna, która upłynęła, nie została zainicjalizowana. Ustaw go na zero powyżej pętli while.

2

Nie wiem, jak to zrobić z modułem "wątku", ale mogę to zrobić za pomocą modułu "wątku". Myślę, że ten kod spełnia to, co chcesz.

uzyskać dokumentację modułu przewlekania 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) 

Należy zauważyć, że użyciu time.time() zamiast time.clock(). time.clock() daje upływ czasu procesora na Uniksie (patrz http://docs.python.org/library/time.html). Myślę, że time.clock() powinien działać wszędzie. Ustawiłem test_time na 10 sekund, ponieważ nie mam cierpliwości przez minutę.

Oto co się dzieje, jeśli I niech go uruchomić pełne 10 sekund:

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

Oto co się dzieje, gdy wpisuję 'zamknąć':

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

Nadzieja to pomaga.

1

Jak wspomniano wyżej, należy użyć modułu threading, jest on znacznie łatwiejszy w użyciu i zapewnia szereg operacji podstawowych synchronizacji. Zapewnia także klasę Timer, która działa po określonym czasie.

Jeśli chcesz tylko, aby program się zakończył, możesz po prostu uczynić z wysyłanego wątku demona. Robisz to, wywołując setDaemon (True) przed wywołaniem start() (2.6 może zamiast tego użyć atrybutu daemon). Python nie zostanie zakończony, dopóki wątek inny niż demon zostanie uruchomiony.

5

Po powrocie skorzystałem z modułu wątków. Jeszcze większą korzyścią jest użycie programu InterruptableThread do zakończenia wątku. Nie musisz używać flagi do zakończenia wątku, ale wyjątek wystąpi, jeśli wywołasz metodę terminate() w tym wątku z nadrzędnego. Możesz obsłużyć wyjątek lub nie.

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: można przepisać kod jak to przy użyciu innego wątku, który czeka 1 minutę, a następnie zabija drugą nitkę

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

Zauważ, że aby ten kod działał w systemach 64-bitowych (tak czy inaczej 64-bitowym Linux), musisz zawinąć pierwszy parametr 'PyThreadState_SetAsyncExc' w wywołaniu' ctypes.c_long'. W przeciwnym razie zostanie przekazany jako 32-bitowa liczba całkowita, która przepełni się, a otrzymasz wyjątek "nieistniejącego wątku" wyjątku ValueError. – intuited

0

Łatwo przetestować zakres 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

Możesz to zrobić całkiem łatwo bez wątków. Na przykład, używając Twisted, wystarczy skonfigurować połączenie z określonym czasem i producent:

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

Ma to różne zalety w stosunku do metody gwintowania. Nie polega na wątkach demonów, więc możesz oczyścić połączenie sieciowe (np. Wysłać wiadomość o zamknięciu, jeśli to konieczne) zamiast polegać na platformie, aby ją zniszczyć. Obsługuje on dla ciebie cały kod sieciowy niskiego poziomu (twoim oryginalnym przykładem jest niewłaściwe działanie w przypadku, gdy socket.send zwróci 0, ten kod poprawnie obsłuży ten przypadek). Nie musisz też polegać na ctypach lub ukrytym API CPython do zgłaszania wyjątku w innym wątku (dzięki czemu jest on przenośny dla większej liczby wersji Pythona i może natychmiastowo przerwać blokowanie wysyłania, w przeciwieństwie do innych sugerowanych podejść).

Powiązane problemy