2010-09-03 19 views
6

Chcę utworzyć serwer i klienta, który wysyła i odbiera pakiety UDP z sieci za pomocą Twisted. Pisałem już to z gniazdami w Pythonie, ale chcę skorzystać z funkcji zwrotnych i wątków Twisted. Jednak potrzebuję pomocy przy projektowaniu Twisted.Klient i serwer UDP z Twisted Python

Mam wiele typów pakietów Chcę otrzymywać, ale niech udawać, że jest tylko jeden:

class Packet(object): 
    def __init__(self, data=None): 
     self.packet_type = 1 
     self.payload = '' 
     self.structure = '!H6s' 
     if data == None: 
      return 

     self.packet_type, self.payload = struct.unpack(self.structure, data) 

    def pack(self): 
     return struct.pack(self.structure, self.packet_type, self.payload) 

    def __str__(self): 
     return "Type: {0}\nPayload {1}\n\n".format(self.packet_type, self.payload) 

Zrobiłem klasę protokołu (prawie kopią przykładach), który wydaje się działać, kiedy wysyłanie danych z innego programu:

class MyProtocol(DatagramProtocol): 
    def datagramReceived(self, data, (host, port)): 
     p = Packet(data) 
     print p 

reactor.listenUDP(3000, MyProtocol()) 
reactor.run() 

Co nie wiem, to w jaki sposób utworzyć klienta, które można wysłać dowolne pakiety w sieci, które odebrano przez reaktorze:

# Something like this: 
s = Sender() 
p = Packet() 
p.packet_type = 3 
s.send(p.pack()) 
p.packet_type = 99 
s.send(p.pack()) 

Muszę również ustawić flagę adresu ponownego użycia na kliencie i serwerach, aby móc uruchamiać wiele instancji jednocześnie na tym samym urządzeniu (np. jeden skrypt wysyła bicie serca, inny reaguje na bicie serca, itp.).

Czy ktoś może mi pokazać, jak można to zrobić z Twisted?

Aktualizacja:

To jak to zrobić z gniazd w Pythonie. Mogę jednocześnie uruchomić wielu słuchaczy i nadawców, a oni wszyscy się słyszą. Jak uzyskać ten wynik z Twisted? (Część słuchania nie musi być oddzielny proces.)

class Listener(Process): 
    def __init__(self, ip='127.0.0.1', port=3000): 
     Process.__init__(self) 
     self.ip = ip 
     self.port = port 

    def run(self): 
     sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 
     sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
     sock.bind((self.ip, self.port)) 

     data, from_ip = sock.recvfrom(4096) 
     p = Packet(data) 
     print p 

class Sender(object): 
    def __init__(self, ip='127.255.255.255', port=3000): 
     self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 
     self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
     self.ip = (ip, port) 

    def send(self, data): 
     self.sock.sendto(data, self.ip) 

if __name__ == "__main__": 
    l = Listener() 
    l.start() 
    s = Sender() 
    p = Packet() 
    p.packet_type = 4 
    p.payload = 'jake' 
    s.send(p.pack()) 

Roztwór roboczy:

class MySender(DatagramProtocol): 
    def __init__(self, packet, host='127.255.255.255', port=3000): 
     self.packet = packet.pack() 
     self.host = host 
     self.port = port 

    def startProtocol(self): 
     self.transport.write(self.packet, (self.host, self.port)) 

if __name__ == "__main__": 
    packet = Packet() 
    packet.packet_type = 1 
    packet.payload = 'jake' 

    s = MySender(packet) 

    reactor.listenMulticast(3000, MyProtocol(), listenMultiple=True) 
    reactor.listenMulticast(3000, s, listenMultiple=True) 
    reactor.callLater(4, reactor.stop) 
    reactor.run() 

Odpowiedz

12

Podobnie jak powyższy przykład serwera, istnieje przykład klienta. To powinno pomóc Ci zacząć:

Ok, tutaj jest prosta nadawca bicie serca i odbiornika za pomocą protokołu datagramów.

from twisted.internet.protocol import DatagramProtocol 
from twisted.internet import reactor 
from twisted.internet.task import LoopingCall 
import sys, time 

class HeartbeatSender(DatagramProtocol): 
    def __init__(self, name, host, port): 
     self.name = name 
     self.loopObj = None 
     self.host = host 
     self.port = port 

    def startProtocol(self): 
     # Called when transport is connected 
     # I am ready to send heart beats 
     self.loopObj = LoopingCall(self.sendHeartBeat) 
     self.loopObj.start(2, now=False) 

    def stopProtocol(self): 
     "Called after all transport is teared down" 
     pass 

    def datagramReceived(self, data, (host, port)): 
     print "received %r from %s:%d" % (data, host, port) 


    def sendHeartBeat(self): 
     self.transport.write(self.name, (self.host, self.port)) 



class HeartbeatReciever(DatagramProtocol): 
    def __init__(self): 
     pass 

    def startProtocol(self): 
     "Called when transport is connected" 
     pass 

    def stopProtocol(self): 
     "Called after all transport is teared down" 


    def datagramReceived(self, data, (host, port)): 
     now = time.localtime(time.time()) 
     timeStr = str(time.strftime("%y/%m/%d %H:%M:%S",now)) 
     print "received %r from %s:%d at %s" % (data, host, port, timeStr) 



heartBeatSenderObj = HeartbeatSender("sender", "127.0.0.1", 8005) 

reactor.listenMulticast(8005, HeartbeatReciever(), listenMultiple=True) 
reactor.listenMulticast(8005, heartBeatSenderObj, listenMultiple=True) 
reactor.run() 

Przykład transmisji po prostu zmienia się wyżej podejście:

from twisted.internet.protocol import DatagramProtocol 
from twisted.internet import reactor 
from twisted.internet.task import LoopingCall 
import sys, time 

class HeartbeatSender(DatagramProtocol): 
    def __init__(self, name, host, port): 
     self.name = name 
     self.loopObj = None 
     self.host = host 
     self.port = port 

    def startProtocol(self): 
     # Called when transport is connected 
     # I am ready to send heart beats 
     self.transport.joinGroup('224.0.0.1') 
     self.loopObj = LoopingCall(self.sendHeartBeat) 
     self.loopObj.start(2, now=False) 

    def stopProtocol(self): 
     "Called after all transport is teared down" 
     pass 

    def datagramReceived(self, data, (host, port)): 
     print "received %r from %s:%d" % (data, host, port) 


    def sendHeartBeat(self): 
     self.transport.write(self.name, (self.host, self.port)) 



class HeartbeatReciever(DatagramProtocol): 
    def __init__(self, name): 
     self.name = name 

    def startProtocol(self): 
     "Called when transport is connected" 
     self.transport.joinGroup('224.0.0.1') 
     pass 

    def stopProtocol(self): 
     "Called after all transport is teared down" 


    def datagramReceived(self, data, (host, port)): 
     now = time.localtime(time.time()) 
     timeStr = str(time.strftime("%y/%m/%d %H:%M:%S",now)) 
     print "%s received %r from %s:%d at %s" % (self.name, data, host, port, timeStr) 



heartBeatSenderObj = HeartbeatSender("sender", "224.0.0.1", 8005) 

reactor.listenMulticast(8005, HeartbeatReciever("listner1"), listenMultiple=True) 
reactor.listenMulticast(8005, HeartbeatReciever("listner2"), listenMultiple=True) 
reactor.listenMulticast(8005, heartBeatSenderObj, listenMultiple=True) 
reactor.run() 
+0

Te przykłady znalazłem sam przy pomocy Google, ale nie rozwiązują one problemów, które mam. – Jake

+0

@Jake Czy to rozwiązuje problem ponownego użycia gniazda lub szukasz czegoś innego? – pyfunc

+0

+1 To działa, ale ponieważ używa multicastu, tylko jeden z reaktorów nasłuchujących odbiera dane wysyłane przez nadawcę. To zbliża mnie trochę do tego, czego szukam, co jest transmitowane do wszystkich klientów słuchających. (Powinieneś opuścić ten przykład, tak jak jest dla osób szukających multiemisji!) – Jake

1

Zapoznaj się z przykładem echoclient_udp.py.

Ponieważ UDP jest prawie symetryczny między klientem a serwerem, po prostu chcesz uruchomić reactor.listenUDP tam też, connect do serwera (który tak naprawdę ustawia domyślny przeznaczenia dla wysyłanych pakietów), a następnie transport.write do wysyłania pakietów.

+0

Sugerujesz Wzywam reactor.listenUDP dwukrotnie (raz z serwerem i raz z klientem), a następnie zadzwonić reactor.run? Nie mogę tego wypróbować, ponieważ nie ustawiłem adresu ponownego użycia, więc nie wiem, czy to faktycznie działa. – Jake

+0

Sugeruję, abyś odsłuchał raz na każdym gnieździe, prawdopodobnie w oddzielnych procesach, a następnie "reactor.run" w każdym procesie. Musisz mieć oddzielną kombinację (ip, port) dla każdego procesu. Nie rozumiem, do czego służy reuseaddr? – poolie