2010-09-03 18 views
6

Je souhaite créer un serveur et un client qui envoie et reçoit des paquets UDP à partir du réseau à l'aide de Twisted. J'ai déjà écrit ceci avec des sockets en Python, mais je veux profiter des fonctionnalités de rappel et de threading de Twisted. Cependant, j'ai besoin d'aide avec le design de Twisted.Client UDP et serveur avec Twisted Python

Je dispose de plusieurs types de paquets que je veux recevoir, mais Feignons il n'y a qu'un seul:

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) 

J'ai fait une classe de protocole (copie presque directe des exemples), ce qui semble fonctionner quand je envoyer des données d'un autre programme:

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

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

Ce que je ne sais pas comment puis-je créer un client qui peut envoyer des paquets arbitraires sur le réseau, qui sont repris par le réacteur:

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

Je dois également m'assurer de définir l'indicateur d'adresse de réutilisation sur le client et les serveurs afin que je puisse exécuter plusieurs instances de chaque en même temps sur le même périphérique (par ex. un script envoie des battements de cœur, un autre répond aux battements de cœur, etc.). Quelqu'un peut-il me montrer comment cela pourrait être fait avec Twisted?

Mise à jour:

Voici comment je le fais avec les sockets en Python. Je peux courir plusieurs auditeurs et expéditeurs en même temps et ils s'entendent tous. Comment obtenir ce résultat avec Twisted? (La partie d'écoute ne doit pas être un processus distinct.)

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

solution de travail:

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

Répondre

12

Tout comme l'exemple de serveur ci-dessus, il existe un exemple de client à. Cela devrait vous aider à démarrer:

Ok, voici est un simple émetteur de battement de coeur et le récepteur en utilisant le protocole de datagrammes.

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

L'exemple de diffusion modifie simplement l'approche ci-dessus:

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

J'ai trouvé ces exemples moi-même avec l'aide de Google, mais ils ne traitent pas les problèmes que je rencontre. – Jake

+0

@Jake Est-ce que cela résout le problème de la réutilisation du socket ou si vous cherchez autre chose? – pyfunc

+0

+1 Cela fonctionne, mais comme il utilise la multidiffusion, seul l'un des réacteurs d'écoute reçoit les données que l'expéditeur est en train d'émettre. Cela me rapproche un peu plus de ce que je cherche, qui est une diffusion pour tous les clients qui écoutent. (Vous devriez laisser cet exemple comme c'est pour les personnes à la recherche de multidiffusion!) – Jake

1

Découvrez l'exemple echoclient_udp.py.

Comme UDP est à peu près symétrique entre le client et le serveur, vous voulez juste courir reactor.listenUDP là aussi, connect au serveur (qui définit vraiment juste la destination par défaut pour les paquets envoyés), puis transport.write pour envoyer vos paquets.

+0

Vous suggérez que j'appelle reactor.listenUDP deux fois (une fois avec le serveur et une fois avec le client), puis appelez reactor.run? Je ne peux pas essayer cela parce que je n'ai pas défini l'adresse de réutilisation donc je ne sais pas si cela fonctionne réellement. – Jake

+0

Je vous suggère d'écouter une fois sur chaque socket, vraisemblablement dans des processus séparés, et ensuite 'reactor.run' dans chaque processus. Vous devez avoir une combinaison distincte (ip, port) pour chaque processus. Je ne comprends pas où reuseaddr vient à cela? – poolie