如何在Python中实现UDP多播?

如何在Python中实现UDP多播?,python,multicast,Python,Multicast,如何在Python中发送和接收UDP多播?是否有一个标准库可以这样做 向多播组广播的多播发送方: #!/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.setsoc

如何在Python中发送和接收UDP多播?是否有一个标准库可以这样做

向多播组广播的多播发送方:

#!/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()
从多播组读取数据并将十六进制数据打印到控制台的多播接收器:

#!/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()
这对我很有用:

接收

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)
发送

它是基于那些不起作用的例子

我的系统是。。。 Linux 2.6.31-15-generic#50 Ubuntu SMP周二11月10日14:54:29 UTC 2009 i686 GNU/Linux Python2.6.4

看看。网络模块可以检查接口是否支持多播(至少在Linux上)

看不到IGMP的问题可能是由于接口不支持多播造成的?

更好地使用:

sock.bind((MCAST_GRP, MCAST_PORT))
而不是:

sock.bind(('', MCAST_PORT))

因为,如果您想在同一端口上侦听多个多播组,您将在所有侦听器上收到所有消息。

tolomea的回答对我很有用。我也把它侵入了:

要使客户机代码(来自tolomea)在Solaris上工作,您需要将
IP\u MULTICAST\u ttl
socket选项的ttl值作为未签名字符传递。否则您将得到一个错误。 这在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))

为了加入多播组,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 socket文档(请参见
Man7IP
)为ip_添加_成员选项引入了两种形式的
ip_mreqn
结构。最短的是8字节长的表单,更长的是12字节长的表单。上面的示例生成了8字节的
setsockopt
调用,其中前四个字节定义了
多播组
,后四个字节定义了
接口ip

,这只是解释其他答案代码中一些细微之处的另一个答案:

  • socket.INADDR\u ANY
    -(已编辑)在
    IP\u ADD\u MEMBERSHIP
    的上下文中,这并不会将套接字绑定到所有接口,而只是选择启动多播的默认接口(根据路由表)
  • 加入多播组与将套接字绑定到本地接口地址不同
有关多播如何工作的更多信息,请参阅

多播接收器:

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)
示例用法:(在两个控制台中运行以下命令并选择您自己的--iface(必须与接收多播数据的接口相同))

python3多播\u recv.py--iface='192.168.56.102'--加入mcast组'224.1.1.1''224.1.1.2''224.1.1.3'--绑定组'224.1.1.2'

python3多播\u recv.py--iface='192.168.56.102'--加入mcast组'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多播\u send.py--mcast组“224.1.1.2”

python3多播\u send.py--mcast组“224.1.1.4”


这个例子不适合我,原因不明

不是晦涩难懂,只是简单的路线

关于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上强制所有多播通信到一个接口

   ifconfig wlp2s0 allmulti
tcpdump非常简单

tcpdump -n multicast
在代码中,您有:

while True:
  # For Python 3, change next line to "print(sock.recv(10240))"
为什么10240

多播数据包大小应为1316字节


我试过这个,但没用。在Wireshark中,我可以看到传输,但我没有看到任何IGMP连接内容,也没有收到任何内容。您需要绑定到多播组/端口,而不是多播地址上的本地端口,
sock.bind((MCAST_GRP,MCAST_port))
出于一个模糊的原因,此示例不适用于我。使用socket.gethostbyname(socket.gethostname())选择接口并不总是选择外部接口——事实上,在debian系统上,它倾向于选择环回地址。Debian在主机表中为主机名添加127.0.1.1条目。相反,使用socket.INADDR\u ANY更有效,排名较高的答案通过“pack”语句使用socket.INADDR\u ANY(比“+”更正确)。此外,正如排名较高的答案正确指出的那样,不需要使用IP_MULTICAST___IF。@BrianBulkowski有许多程序员使用socket.INADDR__ANY,这让我们这些拥有多个接口的人感到非常悲哀和惊愕,他们需要将多播数据放在特定的接口上。解决方案不是socket.INADDR\u ANY。它是通过IP地址选择合适的接口,无论您认为如何最好(一个配置文件,询问最终用户,无论您根据应用程序的需要如何选择)。socket.inaddru ANY将为您获取多播数据,这是事实,如果您假设一个单一宿主主机,那么这是最简单的,但是
   ifconfig wlp2s0 allmulti
tcpdump -n multicast
while True:
  # For Python 3, change next line to "print(sock.recv(10240))"