web-dev-qa-db-ja.com

PythonではどのようにUDPマルチキャストしますか?

PythonでUDPマルチキャストをどのように送受信しますか?そうするための標準ライブラリはありますか?

81
NoName

これは私のために働く:

受け取る

import socket
import struct

MCAST_GRP = '224.1.1.1'
MCAST_PORT = 5007
IS_ALL_GROUPS = True

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if IS_ALL_GROUPS:
    # on this port, receives ALL multicast groups
    sock.bind(('', MCAST_PORT))
else:
    # on this port, listen ONLY to MCAST_GRP
    sock.bind((MCAST_GRP, 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)

送る

import socket

MCAST_GRP = '224.1.1.1'
MCAST_PORT = 5007
# regarding socket.IP_MULTICAST_TTL
# ---------------------------------
# for all packets sent, after two Hops on the network the packet will not 
# be re-sent/broadcast (see https://www.tldp.org/HOWTO/Multicast-HOWTO-6.html)
MULTICAST_TTL = 2

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

http://wiki.python.org/moin/UdpCommunication の例に基づいていますが、動作しませんでした。

私のシステムは... Linux 2.6.31-15-generic#50-Ubuntu SMP Tue Nov 10 14:54:29 UTC 2009 i686 GNU/Linux Python 2.6.4

88
Gordon Wrigley

マルチキャストグループにブロードキャストするマルチキャスト送信者:

#!/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 __== '__main__':
  main()

マルチキャストグループから読み取り、16進データをコンソールに出力するマルチキャストレシーバー:

#!/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 __== '__main__':
  main()
17

より良い使用:

sock.bind((MCAST_GRP, MCAST_PORT))

の代わりに:

sock.bind(('', MCAST_PORT))

同じポートで複数のマルチキャストグループをリッスンする場合、すべてのリスナーですべてのメッセージを取得するためです。

12
st0ne

マルチキャストグループに参加するには、PythonはネイティブOSソケットインターフェイスを使用します。Python環境の移植性と安定性により、ソケットオプションの多くはネイティブソケットsetsockoptに直接転送されますグループメンバーシップへの参加や削除などのマルチキャストモードの操作は、setsockoptのみで実行できます。

マルチキャストIPパケットを受信するための基本プログラムは次のようになります。

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)

まず、ソケットを作成し、バインドし、setsockoptを発行してマルチキャストグループ参加をトリガーします。最後に、パケットを永遠に受信します。

マルチキャストIPフレームの送信は簡単です。単一のNICがシステムにある場合、そのようなパケットの送信は通常のUDPフレームの送信と変わりません。注意する必要があるのは、sendto()に正しい宛先IPアドレスを設定するだけです。方法。

実際、インターネットに関する多くの例が偶然に機能していることに気付きました。公式のpythonドキュメンテーション。それらのすべての問題はstruct.packを誤って使用しています。典型的な例では4slがフォーマットとして使用され、実際のOSソケットと整合しません。インターフェース構造。

pythonソケットオブジェクトに対してsetsockopt呼び出しを実行すると、フードの下で何が起こるかを説明しようとします。

Pythonは、setsockoptメソッド呼び出しをネイティブCソケットインターフェイスに転送します。 Linuxソケットのドキュメント(man 7 ipを参照)は、IP_ADD_MEMBERSHIPオプションのip_mreqn構造の2つの形式を紹介しています。最短形式は8バイト長で、長形式は12バイト長です。上記の例は、8バイトのsetsockopt呼び出しを生成します。ここで、バイトの拳はmulticast_groupと2番目のinterface_ipを定義します。

4
Leszek Wojcik

http://twistedmatrix.com/trac/ からこれを行うフレームワークがあります。以下に例を示します https://twistedmatrix.com/documents/12.2.0/core/howto/udp.html

2
dataq

他の回答のコードのいくつかの微妙なポイントを説明するための別の回答:

---(マルチキャスト(UDP)ソケットをバインドするとはどういう意味ですか? を参照してください。

マルチキャストレシーバー:

import socket
import struct
import argparse


def run(groups, port, iface=None, bind_group=None):
    # generally speaking you want to bind to one of the groups you joined in
    # this script,
    # but it is also possible to bind to group which is added by some other
    # programs (like another python program instance of this)

    # assert bind_group in groups + [None], \
    #     'bind group not in groups to join'
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)

    # allow reuse of socket (to allow another instance of python running this
    # script binding to the same ip/port)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    sock.bind(('' if bind_group is None else bind_group, port))
    for group in groups:
        mreq = struct.pack(
            '4sl' if iface is None else '4s4s',
            socket.inet_aton(group),
            socket.INADDR_ANY if iface is None else socket.inet_aton(iface))

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

    while True:
        print(sock.recv(10240))


if __== '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--port', type=int, default=19900)
    parser.add_argument('--join-mcast-groups', default=[], nargs='*',
                        help='multicast groups (ip addrs) to listen to join')
    parser.add_argument(
        '--iface', default=None,
        help='local interface to use for listening to multicast data; '
        'if unspecified, any interface would be chosen')
    parser.add_argument(
        '--bind-group', default=None,
        help='multicast groups (ip addrs) to bind to for the udp socket; '
        'should be one of the multicast groups joined globally '
        '(not necessarily joined in this python program) '
        'in the interface specified by --iface. '
        'If unspecified, bind to 0.0.0.0 '
        '(all addresses (all multicast addresses) of that interface)')
    args = parser.parse_args()
    run(args.join_mcast_groups, args.port, args.iface, args.bind_group)

使用例:(2つのコンソールで以下を実行し、独自の--ifaceを選択します(マルチキャストデータを受信するインターフェイスと同じである必要があります))

python3 multicast_recv.py --iface='192.168.56.102' --join-mcast-groups '224.1.1.1' '224.1.1.2' '224.1.1.3' --bind-group '224.1.1.2'

python3 multicast_recv.py --iface='192.168.56.102' --join-mcast-groups '224.1.1.4'

マルチキャスト送信者:

import socket
import argparse


def run(group, port):
    MULTICAST_TTL = 20
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
    sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, MULTICAST_TTL)
    sock.sendto(b'from multicast_send.py: ' +
                f'group: {group}, port: {port}'.encode(), (group, port))


if __== '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--mcast-group', default='224.1.1.1')
    parser.add_argument('--port', default=19900)
    args = parser.parse_args()
    run(args.mcast_group, args.port)

使用例:#受信者が以下のマルチキャストグループアドレスにバインドし、一部のプログラムがそのグループへの参加を要求すると仮定します。そして、ケースを簡素化するために、受信者と送信者が同じサブネットの下にあると仮定します

python3 multicast_send.py --mcast-group '224.1.1.2'

python3 multicast_send.py --mcast-group '224.1.1.4'

2
pterodragon

py-multicast をご覧ください。ネットワークモジュールは、インターフェイスがマルチキャストをサポートしているかどうかを確認できます(少なくともLinuxで)。

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

おそらく、IGMPが表示されないという問題は、マルチキャストをサポートしていないインターフェイスが原因でしたか?

2
wroniasty

(tolomeaからの)クライアントコードをSolarisで動作させるには、IP_MULTICAST_TTLソケットオプションのttl値をunsigned charとして渡す必要があります。そうしないと、エラーが発生します。これは、Solaris 10および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))
0
bunnyrabbit