반응형

출처 : https://huichen-cs.github.io/course/CISC7334X/20FA/lecture/pymcast/

Python에서 멀티캐스트 프로그래밍

차례

  • 소개
  • IPv4 멀티캐스트 주소
  • UDP 데이터그램 멀티캐스트
  • 프로그램 예시와 실험 환경
    • 전송 프로그램 (mcastsend.py)
    • 수신 프로그램 (mcastrecv.py)
    • 시연
      • 가정
      • 시연 실행하기
      • 출력
    • 연습과 탐색

소개

통신 패턴을 설명하는 몇 가지 용어가 있습니다. 이러한 용어는 모두 "-cast"로 끝나고 유니캐스트, 브로드캐스트, 멀티캐스트, 애니캐스트 및 지오캐스트를 포함합니다. 여기서는 단일 패킷의 복사본을 가능한 모든 대상의 선택된 하위 집합으로 전달하는 기술인 멀티캐스트에 특히 관심이 있습니다. TCP/IP는 멀티캐스트를 지원합니다. 여기서 우리의 목표는 TCP/IP 프로토콜 스택에서 멀티캐스트의 개념을 설명하기 위해 기초적인 Python 프로그램을 작성하는 것입니다.

IPv4 멀티캐스트 주소

IP 주소에는 몇 가지 범주가 있습니다. 멀티캐스트 주소는 범주입니다. 224.0.0.0/4 범위의 모든 IPv4 주소는 그룹, 즉 우리가 패킷을 전달할 모든 가능한 호스트의 선택된 하위 집합을 정의하는 멀티캐스트 주소입니다. 이 "224.0.0.0/4"와 같은 IP 주소 범위는 소위 CIDR 표기법에 있습니다. 이 특별한 경우 기본적으로 최상위 4비트(또는 4비트 접두사)가 224.0.0.0과 동일한 모든 IPv4 주소가 이 범위에 있음을 의미합니다. (수학적 경사?의 경우 224.0.0.0/4는 {a|∀a,a∧f0000000h ≡ f0000000h} 다음의 IPv4 주소 집합을 정의합니다.

UDP 데이터그램 멀티캐스트

TCP/IP 프로토콜 스택에서 UDP는 데이터그램 멀티캐스트 서비스를 실현합니다.

프로그램 예시와 실험 환경

여기 UDP 데이터그램 멀티캐스트 예시는 두개의 파이썬 프로그램으로 구성됩니다. mcastsend.py는 전송 프로그램이며 mcastrecv.py는 수신 프로그램 입니다. 우리는 4개의 가상 머신을 사용하여 멀티캐스트 개념을 시연하기 위해 프로그램을 실행합니다.

전송 프로그램 (mcastsend.py)

import socket
import sys

def help_and_exit(prog):
    print('Usage: ' + prog + ' host_ip mcast_group_ip mcast_port_num message',
        file=sys.stderr)
    sys.exit(1)

def mc_send(hostip, mcgrpip, mcport, msgbuf):
    # UDP 소켓을 생성한다
    sender = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM, \
            proto=socket.IPPROTO_UDP, fileno=None)
    # 멀티캐스트 (멀티캐스트 그룹 IP 주소, 전송할 포트 번호) 짝인 엔드 포인트를 정의한다.
    mcgrp = (mcgrpip, mcport)

    # 멀티캐스트 데이터그램이 얼마나 많은 홉(hop)을 여행할 수 있는지 정의한다.
    # IP_MULTICAST_TTL를 따로 정의하지 않는다면 기본 값은 1이다. 
    sender.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 1)

    # 멀티캐스트 데이터그램 전송을 담당하는 네트워크 인터페이스(NIC)를 정의합니다.
    # 그렇지 않으면 소켓은 기본 인터페이스를 사용합니다. (루프백이 0이면 ifindex = 1)
    # 데이터그램을 여러 NIC로 전송하려면 각 NIC에 대한 소켓을 만들어야 합니다.
    sender.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, \
         socket.inet_aton(hostip))

    # 버퍼에서 데이터그램을 전송합니다
    sender.sendto(msgbuf, mcgrp)

    # 소켓 자원을 해제합니다 
    sender.close()


def main(argv):
    if len(argv) < 5:
        help_and_exit(argv[0])

    hostipaddr = argv[1]
    mcgrpipaddr = argv[2]
    mcport = int(argv[3])
    msg = argv[4]

    mc_send(hostipaddr, mcgrpipaddr, mcport, msg.encode())

if __name__=='__main__':
    main(sys.argv)

수신 프로그램 (mcastrecv.py)

import sys
import socket
import struct


def help_and_exit(prog):
    print('Usage: ' + prog + ' from_nic_by_host_ip mcast_group_ip mcast_port')
    sys.exit(1)

def mc_recv(fromnicip, mcgrpip, mcport):
    bufsize = 1024

    # UDP 소켓을 생성한다
    receiver = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM, \
            proto=socket.IPPROTO_UDP, fileno=None)

    # 멀티캐스트 (멀티캐스트 그룹 IP 주소, 전송할 포트 번호) 짝인 엔드 포인트로부터 
    # 전송된 데이터그램을 수신하기 위한 소켓을 설정한다.
    # 이 소켓은 전송 프로그램의 짝과 매칭 되어야 한다.
    bindaddr = (mcgrpip, mcport)
    receiver.bind(bindaddr)

    # 소켓을 의도한 멀티캐스트 그룹에 조인합니다. 그 의미는 두 가지입니다.
    # 멀티캐스트 IP 주소에 의해 식별되는 의도된 멀티캐스트 그룹을 지정합니다.
    # 또한 어떤 네트워크 인터페이스에서 (NIC) 소켓은 의도한 멀티캐스트 그룹에 대한 데이터그램을 수신합니다.
    # socket.INADDR_ANY는 시스템의 인터페이스(ifindex = 루프백 인터페이스가 있는 경우 1)에서 
    # 기본 네트워크를 의미한다는 점에 유의하는 것이 중요합니다.
    # 여러 NIC에서 멀티캐스트 데이터그램을 수신하려면
    # 각 NIC용 소켓을 생성해야 합니다. 또한 할당된 IP 주소로 NIC를 식별합니다.
    if fromnicip == '0.0.0.0':
        mreq = struct.pack("=4sl", socket.inet_aton(mcgrpip), socket.INADDR_ANY)
    else:
        mreq = struct.pack("=4s4s", \
            socket.inet_aton(mcgrpip), socket.inet_aton(fromnicip))
    receiver.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)

    # 메세지를 수신한다
    buf, senderaddr = receiver.recvfrom(1024)
    msg = buf.decode()

        # 자원을 해제한다
        receiver.close()

    return msg

def main(argv):
    if len(argv) < 4:
        help_and_exit(argv[0])

    fromnicip = argv[1] 
    mcgrpip = argv[2]
    mcport = int(argv[3])


    msg = mc_recv(fromnicip, mcgrpip, mcport)
    print(msg)

if __name__=='__main__':
    main(sys.argv)

시연

가정

편의를 위해 4개의 리눅스 가상 머신을 다음처럼 이름을 만들고 다음 표에 주어진 IPv4 주소가 있다고 가정합니다.

호스트 이름 IPv4 주소
eastny 192.168.56.1
midwood 192.168.56.3
flatbush 192.168.56.4
bushwick 192.168.56.5

두 개의 멀티캐스트 그룹이 있다고 가정합니다.

IPv4 멀티캐스트 그룹 1 IPv4 멀티캐스트 그룹 2
224.1.1.5 234.3.2.1

전송자는 엔드 포인트를 식별하고 UDP를 사용하기로 결정하였고 4개의 호스트로 다음처럼 두 개의 프로그램을 배포합니다.

호스트 프로그램
midwood mccastsend.py
eastny mccastrecv.py
flatbush mccastrecv.py
bushwick mccastrecv.py

시연 실행하기

우리는 다음처럼 프로그램을 실행합니다.

  1. 첫째, 우리는 다음처럼 eastnyflatbush에서 mcastrecv.py를 실행합니다.
brooklyn@flatbush:~$ python mcastrecv.py 192.168.56.104 224.1.1.5 50001
brooklyn@eastny:~$ python mcastrecv.py 192.168.56.101 224.1.1.5 50001
  1. 우리는 bushwick에서 mcastrecv.pyscapy3를 두 개의 터미널에서 둘 다 실행합니다.
brooklyn@bushwick:~$ python mcastrecv.py 192.168.56.105 234.3.2.1 50001
brooklyn@bushwick:~$ sudo scapy3
>>> packets = sniff(prn=lambda p: p.summary(), filter='udp port 50001')
  1. 마지막으로 우리는 mcastsend.pymidwood에서 실행합니다.
brooklyn@midwood:~$ python mcastsend.py 192.168.56.103 224.1.1.5 50001 "Hello, World!"

출력

출력은 다음과 같습니다.

flatbush에서 우리는 다음을 관찰합니다.

brooklyn@flatbush:~$ python mcastrecv.py 192.168.56.104 224.1.1.5 50001
Hello, World!
brooklyn@flatbush:~$

eastny에서 우리는 다음도 관찰합니다.

brooklyn@eastny:~$ python mcastrecv.py 192.168.56.101 224.1.1.5 50001
Hello, World!
brooklyn@eastny:~$

하지만, bushwick에서 mcastrecv.py는 IPv4 주소 192.168.56.101에 의해 식별되는 네트워크 인터페이스로부터 멀티캐스트 그룹 234.3.2.1로 데이터그램을 받지 못했기에 데이터를 아직 기다립니다. (글쎄, 우리는 이 데모에서 우리는 절대 그 그룹에 데이터그램을 보내지 않는다는 점에 주의해야 합니다.)

`brooklyn@bushwick:~$ python mcastrecv.py 192.168.56.105 234.3.2.1 50001

하지만, scrapy3midwood에서 mcastsend.py에 의해 보내진 패킷을 캡쳐했습니다.

>>> packets = sniff(prn=lambda p: p.summary(), filter='udp port 50001')
Ether / IP / UDP 192.168.56.103:52582 > 224.1.1.5:50001 / Raw / Padding

^C>>> hexdump(packets[0])
0000  01005E010105080027A133B408004500 ..^.....'.3...E.
0010  00290DC74000011191E7C0A83867E001 .)..@.......8g..
0020  0105CD66C351001553C948656C6C6F2C ...f.Q..S.Hello,
0030  20576F726C64210000000000          World!.....
>>>

연습과 탐색

다음 시나리오를 고려해 보겠습니다. 우리는 모든 네트워크 인터페이스로부터 멀티캐스트 데이터그램을 수신하기를 원합니다. socket.INADDR_ANY는 모든 주소를 나타뱀을 관찰할 수 있습니다. 우리는 mcastrecv.py의 간단한 수정으로 이 목표를 달성할 수 있다고 가정합다. 수정은

if fromnicip == '0.0.0.0':
        mreq = struct.pack("=4sl", socket.inet_aton(mcgrpip), socket.INADDR_ANY)
else:
        mreq = struct.pack("=4s4s", \
                socket.inet_aton(mcgrpip), socket.inet_aton(fromnicip))
receiver.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)

에서 다음으로 대체합니다.

mreq = struct.pack("=4sl", socket.inet_aton(mcgrpip), socket.INADDR_ANY)

우리는 eastny 또는 flatbuash에서 다음처럼 수정된 mcastrecv.py를 실행합니다.

brooklyn@flatbush:~$ python mcastrecv.py 192.168.56.104 224.1.1.5 50001

우리는 midwood에서 mcastsend.py를 실행합니다.

brooklyn@midwood:~$ python mcastsend.py 192.168.56.103 224.1.1.5 50001 "Hello, World!"

우리는 mcastsend가 완료할 동안 mcastrecv.py가 데이터를 아직 기다리고 있음을 관찰할 수 있습니다. scapy3를 사용하여 패킷을 캡처하고 캡처된 패킷을 사용하여 현상을 설명하는 실험을 설계합니다.

관련글

반응형
반응형

출처 : https://stackoverflow.com/questions/603852/how-do-you-udp-multicast-in-python

Python에서 UDP 멀티캐스트 하는 방법?

어떻게 Python에서 UDP 멀티캐스트를 송신하고 수신하나요? 이를 하기 위해 표준 라이브러리가 있을까요?


9개의 답변 중 1개

이 코드는 잘 작동합니다.

수신

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:
    # 이 포트로는 모든 멀티캐스트 그룹을 수신합니다.
    sock.bind(('', MCAST_PORT))
else:
    # 이 포트로는 MCAST_GRP IP의 멀티캐스트 그룹만 listen 합니다.
    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:
  # Python 3의 경우 다음 줄을 "print(sock.recv(10240))"로 변경합니다.
  print sock.recv(10240)

송신

import socket

MCAST_GRP = '224.1.1.1'
MCAST_PORT = 5007
# regarding socket.IP_MULTICAST_TTL
# ---------------------------------
# 모든 송신된 패킷에 대해 네트워크에서 2홉을 초과하는 패킷은 재전송/broadcast되지 않을 것입니다.
# (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)

# Python 3의 경우 다음 줄을 'sock.sendto(b"robot", ...'로 변경합니다.
# msg 파라미터는 "바이트 류 객체가 필요합니다" (https://stackoverflow.com/a/42612820)
sock.sendto("robot", (MCAST_GRP, MCAST_PORT))

작동하지 않는 https://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 입니다.

반응형
반응형

출처

https://stackoverflow.com/questions/47415796/multicast-from-tcp-replay-seen-by-wireshark-but-not-by-application


Wireshark에서 tcpreplay의 멀티캐스트가 보이지만 응용프로그램에서는 안 보입니다.

저는 tcpreplay로 진행할 멀티캐스트 패킷 캡쳐를 얻었습니다.

sysctl net.ipv4.conf.all.rp_filter=0
sysctl net.ipv4.conf.eth0.rp_filter=0
tcpreplay -i eth0 --loop=100 new.pcap

Wireshark로 eth0의 트래픽을 관찰하고 제가 관심있는 패킷(224.0.23.60:4937입니다.) 을 볼 수 있었습니다.

하지만 다음 Python 응용프로그램은 패킷을 찾을 수 없습니다.

import socket
import struct

MCAST_GRP = '224.0.23.60'
MCAST_PORT = 4937

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((MCAST_GRP, 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 '#'
   print sock.recv(64)

netstat -g를 실행하면 다음 출력이 나옵니다.

lo              1      all-systems.mcast.net
eth0            1      224.0.23.60

여기에 뭔가 놓친 것이 있습니까?

[편집] 정확히 내 패킷 캡처에서 IP src가 동일한 네트워크 하위 도메인 (ip src : 192.168.1.10)에 없는 반면 내 ip는 146.186.197.164와 비슷합니다.


1개의 답변만 발췌

이 문서(http://tcpreplay.synfin.net/wiki/FAQ)를 주의깊게 읽어 보시면, tcpreplay는 TCP/IP stack과 이더넷 장치 드라이버 사이에 패킷을 보내므로, 호스트 시스템의 TCP/IP 스택에서는 패킷을 볼 수 없습니다.

저는 VirtualBox로 '호스트 전용 어댑터'로 설정된 Debian OS를 사용하여 그 머신에서 tcp를 사용하여 해결하였습니다.


http://tcpreplay.synfin.net/wiki/FAQ 일부 발췌

tcpreplay를 같은 컴퓨터에서 실행하여 패킷을 보낼 수 있습니까?

일반적으로 안 됩니다. tcpreplay가 패킷을 보낼 때, 이 프로그램은 TCP/IP stack과 네트워크 카드의 장치 드라이버 사이에 패킷을 주입합니다. 그 결과, tcpreplay를 실행하는 TCP/IP 시스템은 패킷을 볼 수 없습니다.

한 가지 제안할 수 있는 것은VMWareParallels 또는 Xen 같은 것을 사용하는 것입니다. 가상 머신(guest)에서 tcpreplay를 실행하는 것은 호스트 운영체제에서 패킷을 볼 수 있게 합니다.


반응형
반응형

유선 : 고객에게 서비스를 제공. 멀티캐스트 방식(가입이 필요)

안테나 방송 : 서비스를 지정된 채널로 제공. 브로드캐스트 방식(가입 필요 없음)


14-1. 멀티캐스트(Multicast)

멀티캐스트 전송 방식.

UDP를 기반으로 하는 전송 방식. 

멀티캐스트 그룹(클래스 D에 속하는 IP주소)을 기반으로 멀티캐스트 패킷을 주고 받음.

하나의 멀티캐스트 패킷은 라우터를 통해서 다수의 호스트에 전송.

(한 번만 방송하면 가입자 모두 시청 가능.)

(정보를 단방향으로 제공할 때 주로 사용.)


라우팅(Routing)과 TTL(Time To Live)

라우팅 : 라우터에 의해서 패킷이 경로를 찾는 과정

TTL 

거쳐갈 수 있는 라우터의 수(홉)를 의미. 

이 값이 0이 되면 패킷은 더 이상 전달되지 못하고 소멸된다.


멀티캐스트 Sender와 Receiver

Sender : 멀티캐스트 그룹에 데이터를 전송하는 호스트

Receiver : 멀티캐스트 그룹으로부터 데이터를 수신하는 호스트

※ Sender는 서버(제공자), Receiver는 클라이언트로 구분 지을 수 있지만 요청하는 부분과는 관련이 적다.


멀티캐스트 Sender와 Receiver

 Sender Receiver 
 UDP 소켓 생성
 반드시 TTL 설정(소켓 옵션 설정)
 멀티캐스트 그룹으로 데이터 전송
UDP 소켓 생성
멀티캐스트 그룹 지정(ip_mreq)
멀티캐스트 그룹 가입(소켓 옵션 설정) 

Sender TTL 설정

state = setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_TTL, (void*)&multi_TTL, sizeof(multi_TTL));


Receiver 멀티캐스트 그룹 지정.

struct ip_mreq

{

    struct in_addr imr_multiaddr;    // 가입하고자 원하는 D 클래스 ip주소

    struct in_addr imr_interface;     // 나의 ip 주소. htonl(IN_ADDR);을 써주면 된다.

};

※ 서로 포트는 일치 해야 한다.


14-2. 브로드캐스트(Broadcast)

브로드캐스트 전송방식

UDP를 기반으로 하는 전송 방식(=멀티캐스트와 같다.)

일반적으로 UDP 패킷과의 차이점은 전송 목적지 ip주소 뿐이다.

(멀티캐스트와의 차이점)

동일 네트워크에 속하는 모든 호스트에 동시 전송

인터넷 상에서 지역 네트워크 내에서만 브로드캐스를 허용한다. (네트워크 부하를 고려)


주소선택에 따른 브로드캐스트 방식 구분

지정된 브로드캐스트

예 : 192.12.31.255 -> 자신이 속한 IP주소만 설정 가능. (지역적 브로드캐스트를 더 많이 사용.)


지역적 브로드캐스트 

예 : 255.255.255.255 -> 알아서 현재 네트워크(LAN)에 브로드캐스트.


반응형

+ Recent posts