2010-09-03 15 views
6

Ich möchte einen Server und einen Client erstellen, der UDP-Pakete mit Twisted aus dem Netzwerk sendet und empfängt. Ich habe dies bereits mit Sockets in Python geschrieben, möchte aber Twisteds Callback- und Threading-Funktionen nutzen. Allerdings brauche ich Hilfe beim Design von Twisted.UDP-Client und -Server mit Twisted Python

Ich habe mehrere Arten von Paketen I erhalten wollen, aber lassen Sie uns so tun, gibt es nur eine:

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) 

ich eine Protokollklasse (fast direkte Kopie der Beispiele) hergestellt, die, wenn sie zu arbeiten scheint, ich senden von Daten aus einem anderen Programm:

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

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

Was ich nicht weiß, ist, wie erstelle ich einen Client, der beliebige Pakete im Netzwerk senden können, die durch den Reaktor abgeholt:

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

Ich muss auch sicherstellen, dass die Wiederverwendung Adresse Flag auf dem Client und Servern, so dass ich mehrere Instanzen von jedem zur gleichen Zeit auf dem gleichen Gerät (z. ein Skript sendet Herzschläge, ein anderes reagiert auf Herzschläge usw.).

Kann mir jemand zeigen, wie das mit Twisted gemacht werden könnte?

aktualisieren:

Dies ist, wie ich es mit Steckdosen in Python zu tun. Ich kann mehrere Hörer und Sender gleichzeitig laufen lassen und sie hören sich gegenseitig. Wie bekomme ich dieses Ergebnis mit Twisted? (Der Abhörteil muss kein separater Prozess sein.)

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

Arbeitslösung:

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

Antwort

12

Genau wie im obigen Serverbeispiel gibt es ein Clientbeispiel.

Ok, hier ist ein einfaches Herz höher schlagen Sender und Empfänger mit Datagram Protocol: Dies sollte Ihnen den Einstieg erleichtern.

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

Das Broadcast-Beispiel ändert einfach den oben beschriebenen Ansatz:

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

Ich habe diese Beispiele selbst mit Hilfe von Google gefunden, aber sie lösen nicht die Probleme, die ich habe. – Jake

+0

@Jake Behandelt dies das Problem der Wiederverwendung von Sockets oder Sie suchen nach etwas anderem? – pyfunc

+0

+1 Das funktioniert, aber da es Multicast verwendet, empfängt nur einer der zuhörenden Reaktoren die Daten, die der Absender ausgibt. Das bringt mich ein bisschen näher zu dem, wonach ich suche, was eine Übertragung an alle hörenden Kunden ist. (Sie sollten dieses Beispiel so lassen, wie es für Leute ist, die nach Multicast suchen!) – Jake

1

Schauen Sie sich die echoclient_udp.py Beispiel.

Da UDP ziemlich symmetrisch zwischen Client und Server ist, wollen Sie nur reactor.listenUDP es zu laufen, connect an den Server (das ist wirklich nur das Standardziel für gesendete Pakete setzt), dann transport.write Ihre Pakete zu senden.

+0

Sind Sie vorschlag nenne ich reactor.listenUDP zweimal (einmal mit dem Server und einmal mit dem Kunden) und rufen Sie dann reactor.run? Ich kann das nicht versuchen, weil ich die Wiederverwendungsadresse nicht eingestellt habe, also weiß ich nicht, ob das tatsächlich funktioniert. – Jake

+0

Ich schlage vor, Sie hören einmal auf jeden Sockel, vermutlich in separaten Prozessen, und dann 'reactor.run' in jedem Prozess. Sie müssen eine eindeutige (ip, port) Kombination für jeden Prozess haben. Ich verstehe nicht, wo reuseaddr dazu kommt? – poolie