2010-09-03 27 views
6

Quiero crear un servidor y cliente que envíe y reciba paquetes UDP de la red usando Twisted. Ya he escrito esto con sockets en Python, pero quiero aprovechar las características de devolución de llamada y subprocesamiento de Twisted. Sin embargo, necesito ayuda con el diseño de Twisted.Cliente y servidor UDP con Twisted Python

tengo varios tipos de paquetes que desea recibir, pero vamos a suponer que sólo hay una:

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 (copia casi directa de los ejemplos), que parece funcionar cuando enviar datos desde otro programa:

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 puede enviar paquetes arbitrarios en la red, que son recogidas 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 necesito asegurarme de configurar el indicador de dirección de reutilización en el cliente y los servidores para que pueda ejecutar varias instancias de cada uno al mismo tiempo en el mismo dispositivo (p. un script está enviando latidos, otro responde a latidos del corazón, etc.).

¿Alguien puede mostrarme cómo se puede hacer esto con Twisted?

actualización:

Así es como lo hago con tomas en Python. Puedo ejecutar múltiples oyentes y remitentes al mismo tiempo y todos se escuchan. ¿Cómo obtengo 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() 

Respuesta

12

Al igual que el ejemplo del servidor anterior, hay un ejemplo de cliente para. Esto debería ayudar a empezar:

autorización, aquí es un simple emisor y el receptor latido del corazón utilizando el protocolo de datagramas.

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 difusió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() 
+0

Encontré estos ejemplos con la ayuda de Google, pero no solucionan los problemas que tengo. – Jake

+0

@Jake ¿Resuelve esto el problema de la reutilización de socket o está buscando algo más? – pyfunc

+0

+1 Esto funciona, pero como usa multidifusión, solo uno de los reactores de escucha está recibiendo los datos que el emisor está emitiendo. Eso me acerca un poco más a lo que estoy buscando, que es una transmisión para todos los clientes que escuchan. (¡Debe dejar este ejemplo como a las personas que buscan multidifusión!) – Jake

1

Salida del ejemplo echoclient_udp.py.

Dado que UDP es más o menos simétrica entre cliente y servidor, lo que desea ejecutar reactor.listenUDP allí también, connect al servidor (que en realidad sólo establece el destino predeterminado para los paquetes enviados), entonces transport.write para enviar sus paquetes.

+0

¿Estás sugiriendo que llamo reactor.listenUDP dos veces (una vez con el servidor y una vez con el cliente) y luego llamar reactor.run? No puedo probar eso porque no he configurado la dirección de reutilización, así que no sé si realmente funciona. – Jake

+0

Sugiero que escuches una vez en cada socket, presumiblemente en procesos separados, y luego 'reactor.run' en cada proceso. Necesita tener una combinación distinta (ip, puerto) para cada proceso. No entiendo donde reuseaddr entra en esto? – poolie