PythonでUDPマルチキャストをどのように送受信しますか?そうするための標準ライブラリはありますか?
回答:
これは私のために働きます:
受け取る
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:
# For Python 3, change next line to "print(sock.recv(10240))"
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)
# For Python 3, change next line to 'sock.sendto(b"robot", ...' to avoid the
# "bytes-like object is required" msg (https://stackoverflow.com/a/42612820)
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
sock.bind((MCAST_GRP, MCAST_PORT))
ます。コードが機能する場合と機能しない場合があります。複数のNICがある場合は機能しない場合があります
マルチキャストグループにブロードキャストするマルチキャスト送信者:
#!/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()
マルチキャストグループから読み取り、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 __name__ == '__main__':
main()
sock.bind((MCAST_GRP, MCAST_PORT))
マルチキャストグループに参加するために、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_mreqn
、IP_ADD_MEMBERSHIPオプションの2つの形式の構造が紹介されています。最短の形式は8バイトの長さで、長い形式は12バイトの長さです。上記の例ではsetsockopt
、最初の4バイトが定義しmulticast_group
、次の4バイトが定義する8バイトの呼び出しを生成しますinterface_ip
。
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が表示されないという問題は、マルチキャストをサポートしていないインターフェイスが原因でしたか?
他の回答のコードのいくつかの微妙な点を説明するためのちょうど別の回答:
socket.INADDR_ANY
-(編集済み)のコンテキストではIP_ADD_MEMBERSHIP
、これは実際にはソケットをすべてのインターフェイスにバインドするのではなく、マルチキャストがアップしているデフォルトのインターフェイスを選択するだけです(ルーティングテーブルによる)マルチキャスト(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 __name__ == '__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 __name__ == '__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'
(tolomeaからの)クライアントコードをSolarisで機能させるには、IP_MULTICAST_TTL
socketオプションのttl値をunsignedcharとして渡す必要があります。そうしないと、エラーが発生します。これは、Solaris10および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))
この例は、あいまいな理由で私には機能しません。
あいまいではありません、それは単純なルーティングです。
OpenBSDの場合
route add -inet 224.0.0.0/4 224.0.0.1
Linuxの開発者へのルートを設定できます
route add -net 224.0.0.0 netmask 240.0.0.0 dev wlp2s0
Linux上の1つのインターフェースにすべてのマルチキャストトラフィックを強制する
ifconfig wlp2s0 allmulti
tcpdumpは非常にシンプルです
tcpdump -n multicast
あなたのコードには次のものがあります:
while True:
# For Python 3, change next line to "print(sock.recv(10240))"
なぜ10240?
マルチキャストパケットサイズは1316バイトである必要があります
トロメアの答えは私のために働いた。私もそれをsocketserver.UDPServerにハックしました:
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)