2009-03-02 6 views
68

Comment envoyer et recevoir des données UDP Multicast en Python? Existe-t-il une bibliothèque standard pour le faire?Multicast en Python

Répondre

-1

Le trafic multicast n'est pas différent de l'UDP normal, sauf pour l'adresse IP. Jetez un oeil à la norme socket library. Vous pourriez être en mesure de trouver quelque chose qui s'appuie sur socket et est plus facile à utiliser.

+0

Droite. Mais que diriez-vous de rejoindre un groupe? Je ne souhaite pas lancer ma propre gestion de groupe, si possible. –

+7

Le trafic multidiffusion est différent du trafic UDP normal (unicast): vous devez rejoindre le groupe de multidiffusion, tous les commutateurs et routeurs concernés doivent gérer les implications, la TTL est importante et n'est généralement pas acheminée via le WAN. –

16

émetteur Multicast qui diffuse à un groupe de multidiffusion:

#!/usr/bin/env python 

import socket 
import struct 

def main(): 
    MCAST_GRP = '224.1.1.1' 
    MCAST_PORT = 5007 
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) 
    sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 32) 
    sock.sendto('Hello World!', (MCAST_GRP, MCAST_PORT)) 

if __name__ == '__main__': 
    main() 

récepteur Multicast qui lit un groupe de multidiffusion et imprime des données hexagonaux sur la console:

#!/usr/bin/env python 

import socket 
import binascii 

def main(): 
    MCAST_GRP = '224.1.1.1' 
    MCAST_PORT = 5007 
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) 
    try: 
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
    except AttributeError: 
    pass 
    sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 32) 
    sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 1) 

    sock.bind((MCAST_GRP, MCAST_PORT)) 
    host = socket.gethostbyname(socket.gethostname()) 
    sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(host)) 
    sock.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, 
        socket.inet_aton(MCAST_GRP) + socket.inet_aton(host)) 

    while 1: 
    try: 
     data, addr = sock.recvfrom(1024) 
    except socket.error, e: 
     print 'Expection' 
     hexdata = binascii.hexlify(data) 
     print 'Data = %s' % hexdata 

if __name__ == '__main__': 
    main() 
+0

J'ai essayé ça, ça n'a pas marché. Dans Wireshark, je peux voir la transmission, mais je ne vois pas de jointure IGMP et je ne reçois rien. –

+1

vous devez vous connecter au port de multidiffusion/port non local sur l'adresse de multidiffusion, 'sock.bind ((MCAST_GRP, MCAST_PORT))' – stefanB

+0

Cet exemple ne fonctionne pas pour moi, pour une raison obscure. L'utilisation de socket.gethostbyname (socket.gethostname()) pour sélectionner l'interface n'élit pas toujours l'interface externe - en fait, sur les systèmes debian, elle tend à sélectionner l'adresse de bouclage. Debian ajoute une entrée de 127.0.1.1 dans la table hôte pour le nom d'hôte. Au lieu de cela, il est plus efficace d'utiliser socket.INADDR_ANY, que la réponse de plus haut niveau utilise via l'instruction 'pack' (ce qui est plus correct que le '+'). En outre, l'utilisation de IP_MULTICAST_IF n'est pas requise, comme l'indique correctement la réponse de niveau supérieur. –

78

Cela fonctionne pour moi:

Recevoir

import socket 
import struct 

MCAST_GRP = '224.1.1.1' 
MCAST_PORT = 5007 

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) 
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
sock.bind(('', MCAST_PORT)) # use MCAST_GRP instead of '' to listen only 
          # to MCAST_GRP, not all groups on MCAST_PORT 
mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY) 

sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) 

while True: 
    print sock.recv(10240) 

Envoyer

import socket 

MCAST_GRP = '224.1.1.1' 
MCAST_PORT = 5007 

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) 
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2) 
sock.sendto("robot", (MCAST_GRP, MCAST_PORT)) 

Il est basé sur les exemples de http://wiki.python.org/moin/UdpCommunication qui ne fonctionnent pas.

Mon système est ... Linux 2.6.31-15-générique # 50 Ubuntu SMP Mar 10 novembre 14:54:29 UTC 2009 i686 GNU/Linux Python 2.6.4

+6

Pour mac os x, vous devez utiliser l'option socket.SO_REUSEPORT comme alternative à socket.SO_REUSEADDR dans l'exemple ci-dessus, pour permettre plusieurs écouteurs sur la même combinaison d'adresses de port de multidiffusion. – atikat

+0

Pour l'envoi, j'ai également besoin de "sock.bind ((, 0))" parce que mon écouteur de multidiffusion était lié à un adaptateur spécifique. –

+2

pour multidiffusion udp vous devez vous lier au groupe/port de multidiffusion pas au port de groupe local, 'sock.bind ((MCAST_GRP, MCAST_PORT))', votre code peut ne pas fonctionner, il peut ne pas fonctionner lorsque vous avez plusieurs nics – stefanB

2

Jetez un oeil au py-multicast. Le module réseau peut vérifier si une interface supporte la multidiffusion (sur Linux au moins).

import multicast 
from multicast import network 

receiver = multicast.MulticastUDPReceiver ("eth0", "238.0.0.1", 1234) 
data = receiver.read() 
receiver.close() 

config = network.ifconfig() 
print config['eth0'].addresses 
# ['10.0.0.1'] 
print config['eth0'].multicast 
#True - eth0 supports multicast 
print config['eth0'].up 
#True - eth0 is up 

Peut-être des problèmes avec ne pas voir IGMP, ont été causés par une interface ne supportant pas la multidiffusion?

+0

http://coobs.eu.org/py-multicast/ Le lien est cassé – balki

+2

donc c'est :(, pour l'instant vous pouvez essayer ceci: http://pypi.python.org/pypi/py-multicast – wroniasty

8

mieux utiliser:

sock.bind((MCAST_GRP, MCAST_PORT)) 

au lieu de:

sock.bind(('', MCAST_PORT)) 

... parce que si vous voulez écouter plusieurs groupes MCAST sur le même port, vous obtiendrez tous les messages sur tous les auditeurs

0

La réponse de tolomea a fonctionné pour moi. Je piraté dans socketserver.UDPServer aussi:

class ThreadedMulticastServer(socketserver.ThreadingMixIn, socketserver.UDPServer): 
    def __init__(self, *args): 
     super().__init__(*args) 
     self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) 
     self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
     self.socket.bind((MCAST_GRP, MCAST_PORT)) 
     mreq = struct.pack('4sl', socket.inet_aton(MCAST_GRP), socket.INADDR_ANY) 
     self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) 
0

Pour rendre le code client (de Tolomea) fonctionner sous Solaris vous devez passer la valeur de l'option ttl socket IP_MULTICAST_TTL comme un unsigned char. Sinon, vous obtiendrez une erreur. Cela a fonctionné pour moi sur Solaris 10 et 11:

import socket 
import struct 

MCAST_GRP = '224.1.1.1' 
MCAST_PORT = 5007 
ttl = struct.pack('B', 2) 

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) 
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl) 
sock.sendto("robot", (MCAST_GRP, MCAST_PORT)) 
1

Pour Rejoindre le groupe de multidiffusion Python utilise l'interface socket OS natif.En raison de la portabilité et de la stabilité de l'environnement Python, de nombreuses options de socket sont directement transmises à l'appel setsockopt du socket natif. Le mode de fonctionnement multidiffusion, tel que l'appartenance à un groupe et l'abandon de groupe, peut être accompli par setsockopt uniquement.

programme de base pour recevoir paquets IP de multidiffusion peut ressembler à:

from socket import * 

multicast_port = 55555 
multicast_group = "224.1.1.1" 
interface_ip = "10.11.1.43" 

s = socket(AF_INET, SOCK_DGRAM) 
s.bind(("", multicast_port)) 
mreq = inet_aton(multicast_group) + inet_aton(interface_ip) 
s.setsockopt(IPPROTO_IP, IP_ADD_MEMBERSHIP, str(mreq)) 

while 1: 
    print s.recv(1500) 

Tout d'abord, il crée socket, lie et déclenche déclenche le groupe de multidiffusion se joindre en émettant setsockopt. À la toute fin, il reçoit des paquets pour toujours.

L'envoi de trames IP multicast est direct. Si vous avez une seule carte réseau dans votre système, l'envoi de ces paquets ne diffère pas de l'envoi habituel des trames UDP. Tout ce dont vous avez besoin est de régler l'adresse IP de destination correcte dans la méthode sendto().

J'ai remarqué que beaucoup d'exemples autour d'Internet fonctionnent en fait par accident. Même sur la documentation officielle de Python. Problème pour tous d'utiliser incorrectement struct.pack. Veuillez noter qu'un exemple typique utilise le format 4sl et qu'il n'est pas aligné avec la structure d'interface de socket du système d'exploitation.

Je vais essayer de décrire ce qui se passe sous le capot lors de l'exercice setsockopt pour l'objet socket python.

Python transmet l'appel de la méthode setsockopt à l'interface de socket C native. La documentation de socket Linux (voir man 7 ip) introduit deux formes de structure ip_mreqn pour l'option IP_ADD_MEMBERSHIP. Le format le plus court est de 8 octets et le plus long de 12 octets. L'exemple ci-dessus génère 8 octets setsockopt appel où poing pour octets définit multicast_group et deuxième interface_ip.