Cliente y servidor UDP con Twisted Python

Quiero crear un servidor y un cliente que envíe y reciba paquetes UDP de la red usando Twisted. Ya lo he escrito con sockets en Python, pero quiero aprovechar las funciones de callback y subprocesos de Twisted. Sin embargo, necesito ayuda aunque con el diseño de Twisted.

Tengo varios tipos de paquetes que quiero recibir, pero supongamos que solo hay uno:

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) 

Hice una clase de protocolo (casi copia directa de los ejemplos), que parece funcionar cuando envío datos desde otro progtwig:

 class MyProtocol(DatagramProtocol): def datagramReceived(self, data, (host, port)): p = Packet(data) print p reactor.listenUDP(3000, MyProtocol()) reactor.run() 

Lo que no sé es cómo puedo crear un cliente que pueda enviar paquetes arbitrarios en la red, que sean recogidos por el reactor:

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

También debo asegurarme de establecer el indicador de reutilización en el cliente y los servidores para poder ejecutar varias instancias de cada una al mismo tiempo en el mismo dispositivo (por ejemplo, un script envía latidos, otro responde a latidos, etc.).

¿Puede alguien mostrarme cómo se podría hacer esto con Twisted?

Actualización :

Así es como lo hago con sockets en Python. Puedo ejecutar múltiples escuchas y remitentes al mismo tiempo y todos se escuchan entre sí. ¿Cómo consigo este resultado con Twisted? (La parte de escucha no tiene por qué ser un proceso separado.)

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

Solución de trabajo :

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

Al igual que en el ejemplo de servidor anterior, hay un ejemplo de cliente para. Esto debería ayudarte a comenzar:

Ok, aquí hay un simple remitente y receptor de ritmo cardíaco que utiliza el protocolo de datagtwigs.

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

El ejemplo de transmisión simplemente modifica el enfoque anterior:

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

Echa un vistazo al ejemplo echoclient_udp.py .

Dado que UDP es bastante simétrico entre el cliente y el servidor, solo desea ejecutar reactor.listenUDP allí, connect al servidor (que realmente solo establece el destino predeterminado para los paquetes enviados), luego transport.write para enviar sus paquetes.