수업/Network Programming

[Network Programming] Multicast & Broadcast

hw-ani 2023. 5. 29. 11:03

> Multicast

Multicast 데이터 전송 방식

    - 특정 Multicast 그룹을 대상으로 데이터를 딱 한번 전송

    - 그럼 해당 그룹에 속하는 client는 모두 데이터를 수신한다.

    - Multicast 그룹 수는 IP 주소 범위 내에서 얼마든 추가 가능

    - 특정 Multicast 그룹으로 전송되는 데이터를 수신하려면 거기에 가입하면 된다.

    - Multicast에선 연결의 개념이 없으므로 UDP socket을 기반으로 전송

    - 모든 host에 데이터를 전송해야할때 그냥 TCP나 UDP를 쓰는 것보다 트래픽 양 ↓

 

unicasting을 여러번 하면 네트워크 상에 패킷이 너무 많다.

 

 

앞서 IPv4 주소를 설명할 때, Class D(224.0.0.0~239.255.255.255)의 주소들이 Multicast를 위한 주소라고 했다.

여기서 각 IP 주소가 각 Multicast 그룹을 나타낸다. 즉, 특정 Multicast 그룹에 가입한다는 것은 다음과 같이 외치는 것으로 이해할 수 있다. "난 class D에 속하는 IP 주소 중 xxx.xxx.xxx.xxx로 전송되는 멀티캐스트 데이터에 관심있으니 이 데이터를 수신하겠다!"

 

> Multicast routing protocols & IGMP (Internet Group Management Protocol)

: Multicast 그룹의 멤버를 찾기 위해서 위 두가지 방식을 모두 사용한다.

: 전자는 globally한 영역에서 찾을 때 사용하고, 후자는 locally한 영역에서 찾을 때 사용한다.

 

Multicast의 이점이 너무 커보인다. 하지만 Multicast을 지원하는 것은 비용이 꽤 크기 때문에, 아직도 적지 않은 수의 라우터가 멀티캐스트를 애초에 지원하지 않거나 일부러 막아두는 경우가 있다.
멀티캐스트를 지원하지 않는 라우터를 거쳐서 멀티캐스트 패킷을 전달하기 위해 Tunneling이라는 기술도 사용된다.(물론 이런건 멀티캐스트 기반 응용프로그램 개발자가 고민할 문제는 아니다.)
보통 라우터들은 유니캐스트만 지원하는게 일반적이므로, 멀티캐스트 지원하는 애들끼리 묶은 MBONE(Multicast backBONE)이란 것도 있다.

 

 

 

 

TTL(Time to Live)

TTL은 정수로 표현되며 router를 하나 거칠 때마다 1씩 감소한다. 오류로 인해 패킷이 도착지에 가지 못하고 계속 같은 구간을 순회하는 경우를 막기 위해서 사용한다. TTL이 0이 되면 해당 패킷은 소멸된다.

Multicast 패킷을 전송하려면 이 TTL을 설정해줘야 한다. - 보내는 놈은 이 설정해야 함.

 

int send_sock;
send_sock = socket(PF_INET, SOCK_DGRAM, 0);

int time_live = 64;
setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_TTL, (void*)&time_live, sizeof(time_live);
//TTL이 64로 설정되었다.

IPPROTO_IP > IP_MULTICAST_TTL   옵션의 값을 64로 설정

 

 

 

Multicast 그룹 가입

Multicast 패킷을 받으려면 해당 그룹에 가입해야 한다. - 받는 놈은 이 설정해야 함.

int recv_sock;
recv_sock = socket(PF_INET, SOCK_DGRAM, 0);

//가입할 그룹의 정보를 저장할 구조체
struct ip_mreq join_adr;
join_adr.imr_multiaddr.s_addr = "멀티캐스트 그룹의 주소 정보";
join_adr.imr_interface.s_addr = "그룹에 가입할 host의 주소 정보";

//setsockopt로 그룹에 가입
setsockopt(recv_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void*)&join_adr, sizeof(join_adr));


////////////////////////////////////////////////////////////
// 참고: struct ip_mreq 선언
struct ip_mreq {
    struct in_addr imr_multiaddr;
    struct in_addr imr_interface;
};

IPPROTO_IP > IP_ADD_MEMBERSHIP   옵션에 가입할 멀티캐스트 그룹 정보를 담은 구조체를 넘겨주며 가입

 

 

 

> Multicast Sender 예시

//multicast sender code의 일부
//argv[1]:데이터를 보낼 멀티캐스트 주소, argv[2]:사용할 포트번호

//소켓생성 및 멀티캐스트 주소 세팅
send_sock = socket(PF_INET, SOCK_DGRA, 0);
memset(&mul_adr, 0, sizeof(mul_adr));
mul_adr.sin_family = AF_INET;
mul_adr.sin_addr.s_addr = inet_addr(argv[1]);
mul_adr.sin_port = htons(atoi(argv[2]));
//inet_addr 반환 type이 in_addr_t라서(즉, 같은 타입이라서) 바로 assign 가능

//multicast를 위한 소켓 옵션 설정
setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_TTL,
           (void*)&time_live, sizeof(time_live));

//multicast 주소로 쏴버리기
sendto(send_sock, buf, strlen(buf), 0
       (struct sockaddr*)&mul_adr, sizeof(mul_adr));

 

 

> Multicast Receiver 예시

//multicast receiver code의 일부
//argv[1]:가입할 멀티캐스트 주소, argv[2]:사용할 포트번호

//소켓생성 및 bind할 주소 세팅
recv_sock = socket(PF_INET, SOCK_DGRAM, 0);
memset(&adr, 0, sizeof(adr));
adr.sin_family = AF_INET;
adr.sin_addr.s_addr = htonl(INADDR_ANY);
adr.sin_port = htons(atoi(argv[2]));

//데이터를 받기 위해 세팅한 주소 정보와 bind()
if (bind(recv_sock, (struct sockaddr*)&adr, sizeof(adr)) == -1)
    error_handling("bind() error");

//multicast 가입을 위한 주소 설정
join_adr.imr_multiaddr.s_addr = inet_addr(argv[1]);
join_adr.imr_interface.s_addr = htonl(INADDR_ANY);

//multicast 가입을 위한 소켓 옵션 설정
setsockopt(recv_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
           (void*)&join_adr, sizeof(join_adr));

//multicast 주소로 오는 데이터 받기
str_len = recvfrom(recv_sock, buf, strlen(buf), 0, NULL, 0);
//발신지 정보는 필요없어서 NULL, 0 으로 설정

 

 


> Broadcast

Boradcast란, 동일한 네트워크 내의 모든 호트스에게 데이터를 전송하는 방법이다. 동일한 네트워크란 라우터로 끊겨있는 영역을 말한다. 마찬가지로 UDP 소켓을 기반으로 하며, 데이터 전송 대상은 host가 아니라 네트워크이다.

 

- Directed Broadcast

  : IP에서 네트워크 주소를 제외한 호스트 주소를 다 1로하면 그 네트워크로 데이터가 전송(broadcast)된다.

- Local Broadcast

  : 255.255.255.255로 데이터를 전송하면, 전송한 호스트가 속한 네트워크로 데이터가 전송(broadcast)된다.

 

 

int send_sock;
send_sock = socket(PF_INET, SOCK_DGRAM, 0);

int bcast = 1;
setsockopt(send_sock, SOL_SOCKET, SO_BROADCAST, (void*)&bcast, sizeof(bcast));

Broadcast가 가능하려면, socket의 SOL_SOCKET > SO_BROADCAST 옵션을 1로 변경해야한다.

 

 

 

> Broadcast Sender 예시

//broadcast sender code의 일부
//argv[1]:데이터를 보낼 브로드캐스트 주소, argv[2]:사용할 포트번호

//소켓 생성 및 브로드캐스트 주소 세팅
send_sock = socket(PF_INET, SOCK_DGRAM, 0);
memset(&broad_adr, 0, sizeof(broad_adr));
broad_adr.sin_family = AF_INET;
broad_adr.sin_addr.s_addr = inet_addr(argv[1]);
borad_adr.sin_port = htons(atoi(argv[2]));

//broadcast를 위한 소켓 옵션 설정
setsockopt(send_sock, SOL_SOCKET, SO_BROADCAST, (void*)&so_brd, sizeof(so_brd));

//boradcast 주소로 쏴버리기
sendto(send_sock, buf, strlen(buf), 0,
       (struct sockaddr*)&broad_adr, sizeof(broad_adr));

 

 

> Boradcast Receiver 예시

//broadcast receiver code의 일부
//argv[1]:사용할 포트번호

//소켓 생성 및 bind()를 위한 주소 세팅
recv_sock = socket(PF_INET, SOCK_DGRAM, 0);
memset(&adr, 0, sizeof(broad_adr));
adr.sin_family = AF_INET;
adr.sin_addr.s_addr = htonl(INADDR_ANY);
adr.sin_port = htons(atoi(argv[1]));

//그냥 오는 데이터 받는거니까 소켓 옵션 설정할 필요 없음

//오는 데이터 받기
recvfrom(recv_sock, buf, strlen(buf), 0, NULL, 0);
//발신지 정보는 필요없어서 NULL, 0 으로 설정