[python] Python에서 UDP 멀티 캐스트는 어떻게합니까?

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


답변

멀티 캐스트 그룹에 브로드 캐스트하는 멀티 캐스트 발신자 :

#!/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))

대신에:

sock.bind(('', 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 주소를 설정하는 것뿐입니다 .

사실 인터넷에 대한 많은 예가 우연히 작동한다는 것을 알았습니다. 공식 파이썬 문서에서도. 그들 모두에 대한 문제는 struct.pack을 잘못 사용하고 있습니다. 일반적인 예제는 4sl형식으로 사용 되며 실제 OS 소켓 인터페이스 구조와 일치하지 않습니다.

파이썬 소켓 객체에 대한 setsockopt 호출을 실행할 때 후드 아래에서 일어나는 일을 설명하려고 노력할 것입니다.

Python은 setsockopt 메서드 호출을 네이티브 C 소켓 인터페이스로 전달합니다. Linux 소켓 설명서 (참조 man 7 ip)에서는 ip_mreqnIP_ADD_MEMBERSHIP 옵션에 대한 두 가지 형태의 구조를 소개 합니다. 가장 짧은 형식은 길이가 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)

샘플 사용 : (아래 두 개의 콘솔에서 실행하고 자신의 –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소켓 옵션에 대한 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))