나의 브을로오그으

#14. 멀티캐스트 & 브로드캐스트 본문

네트워크/열혈 TCP_IP 소켓프로그래밍

#14. 멀티캐스트 & 브로드캐스트

__jhp_+ 2022. 9. 6. 09:06

#1. 멀티캐스트(Multicast)

멀티캐스트 방식의 데이터 전송은 UDP를 기반으로 한다. 따라서 UDP 서버/클라이언트의 구현방식이 매우 유사하다.  차이점이 있다면 UDP에서의 데이터 전송은 하나의 목적지를 두고 이뤄지지만 멀티캐스트에서의 데이터 전송은 특정 그룹에 가입(등록)되어 있는 다수의 호스트가 된다는 점이다. 즉, 멀티캐스트 방식을 이용하면 단 한번에 데이터 전송으로 다수의 호스트에게 데이터를 전송할 수 있다.

 

 

멀티캐스트의 데이터 전송방식과 멀티캐스트 트래픽 이점

- 멀티캐스트 서버는 특정 멀티캐스트 그룹을 대상으로 데이터를 딱 한번 전송한다.

- 딱! 한번 전송하더라도 그룹에 속하는 클라이언트는 모두 데이터를 수신한다.

- 멀티캐스트 그룹의 수는 IP주소 범위 내에서 얼마든지 추가가 가능하다.

- 특정 멀티캐스트 그룹으로 전송되는 데이터를 수신하려면 해당 그룹에 가입하면 된다.

 

여기서 말하는 멀티캐스트 그룹이란 클래스 D에 속하는 IP주소(224.0.0.0 ~ 239.255.255.255)에 해당하는 그룹원을 의미한다. 따라서 멀티캐스트 그룹에 가입을 한다는 것은 프로그램 코드 상에서 다음과 같이 외치는 것 정도로 이해할 수 있다.

"나는 클래스 D에 속하는 IP주소 중에서 239.234.218.234를 목적지로 전송되는 멀티캐스트 데이터에 관심이 있으므로, 이 데이터를 수신하겠다."

멀티캐스트는 UDP를 기반으로 하므로 멀티캐스트 패킷은 그 형태가 UDP 패킷과 동일하다. 다만 일반적인 UDP 패킷과 달리 하나의 패킷만 네트워크상에 띄워 놓으면 라우터들은 이 패킷을 복사해서 다수의 호스트에 이를 전달한다. 이렇듯 멀티캐스트는 라우터의 도움으로 완성된다. (인터넷은 일종의 라우터의 집합이며, 하나의 라우터가 형성하고 있는 네트워크를 특정 그룹이라고 보면 될것 같다.)

 

만약 요청 클라이언트마다 동일한 데이터를 전송해야 한다면 트래픽 측면에서 매우 부정적일 수 밖에 없다. 그러나 멀티캐스트 기술을 사용하면 하나의 영역에 동일한 패킷을 전달함으로써 그 영역에 속한 모든 클라이언트들이 데이터를 수신한다는 데에서 효율적이라고 볼 수 있다.

TCP 또는 UDP 방식으로 1,000개의 호스트에 파일을 전송하려면, 총 1,000회 파일을 전송해야 한다. 열 개의 호스트가 하나의 네트워크로 묶여 있어서 경로의 99%가 일치한다고 하더라도 말이다. 하지만 멀티캐스트 방식으로 파일을 전송하면 딱 한번만 전송해주면 된다. 1,000개의 호스트를 묶고 있는 라우터가 1,000개의 호스트에게 파일을 복사해 줄 테니 말이다. 이러한 성격때문에 멀티캐스트 방식의 데이터 전송은 "멀티미디어 데이터의 실시간 전송"에 주로 사용된다.

그렇다면 이론상으로 멀티캐스트가 쉽게 되어야 하지만, 적지 않은 수의 라우터가 멀티캐스트를 지원하지 않거나 지원하더라도 네트워크의 불필요한 트래픽 문제를 고려해서 일부러 막아 놓은 경우가 많다. 때문에 멀티캐스트를 지원하지 않는 라우터를 거쳐서 멀티캐스트 패킷을 전송하기 위한 터널링(Tunneling)기술이라는 것도 사용된다. (이는 멀티캐스트 기반의 응용 프로그램 개발자가 고민할 문제는 아니다.) 어찌됐든 여기서는 멀티캐스트 서비스가 가능한 환경이 구축되어 있는 상황에서의 프로그래밍 방법에 대해서만 이야기하고자 한다.

 

 

라우팅(Routing)과 TTL(Time to Live),  그리고 그룹으로의 가입방법

멀티캐스팅 패킷을 보내기 위해서는 TTL이라는 것의 설정과정을 반드시 거쳐야 한다. TTL은 Time to Live의 약자로써 '패킷을 얼마나 멀리 전달할 것인가'를 결정하는 주 요소가 된다. TTL은 정수로 표현되며, 이 값은 라우터를 하나 거칠 때마다 1씩 감소한다. 그리고 이 값이 0이 되면 패킷은 더 이상 전달되지 못하고 소멸된다. 따라서 TTL을 너무 크게 설정하면 네트워크 트래픽에 좋지 못한 영향을 줄 수 있다. 물론 너무 적게 설정해도 목적지에 도달하지 않는 문제가 발생할 수 있으니 주의해야 한다.

 

TTL 설정은 setsockopt 함수를 통해 설정할 수 있다. TTL의 설정과 관련된 프로토콜의 레벨은 IPPROTO_IP이고 옵션의 이름은 IP_MULTICAST_TTL이다. 따라서 TTL을 64로 설정하고자 할 때에는 다음과 같이 코드를 작성한다.

int send_sock;
int time_live = 64;
.....
send_sock = socket(PF_INET, SOCK_DGRAM, 0);
setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_TTL, (void*)&time_live, sizeof(time_live));
.....

 

멀티캐스트 그룹의로의 가입 역시 소켓의 옵션설정을 통해 이뤄진다. 그룹 가입과 관련된 프로토콜의 레벨은 IPPROTO_IP이고, 옵션의 이름은 IP_ADD_MEMBERSHIP이다.

int recv_sock;
struct ip_mreq join_adr;
.....
recv_sock = socket(PF_INET, SOCK_DGRAM, 0);
.....
join_adr.imr_multiaddr.s_addr = "멀티캐스트 그룹의 주소정보";
join_adr.imr_interface.s_addr = "그룹에 가입할 호스트의 주소정보";
setsockopt(recv_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void*)&join_adr, sizeof(join_adr));
.....

 

여기서 쓰인 ip_mreq의 구조체 정보는 다음과 같다.

struct ip_mreq
{
    struct in_addr imr_multiaddr;
    struct in_addr imr_interface;
}

imr_multiaddr : 가입할 그룹의 IP 주소를 채워 넣는다.

imr_interface : 그룹에 가입하는 소켓이 속한 호스트의 IP주소를 명시한다. 이때 INADDR_ANY를 이용하는 것도 가능하다.

 

멀티캐스트 Sender와 Receiver의 구현

멀티캐스트 기반에서는 서버, 클라이언트라는 표현을 대신해서 전송자(이하 Sender라 표시한다.), 수신자(이하 Receiver라 표현한다)라는 표현을 사용한다.여기서 Sender는 말 그대로 멀티캐스트 데이터의 전송주체이고, Receiver는 멀티캐스트 그룹의 가입과정이 필요한 데이터의 수신주체

- Sender : 파일에 저장된 뉴스 정보를 AAA 그룹으로 방송 (Broadcasting)한다.

- Receiver : AAA 그룹으로 전송된 뉴스정보를 수신한다.

 

[news_sender.c]

/* linux */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define		 TTL		64
#define		 BUF_SIZE	30
void error_handling(char* msg);

int main(int argc, char* argv[])
{
	int send_sock;
	struct sockaddr_in mul_adr;
	int time_live = TTL;
	FILE* fp;
	char buf[BUF_SIZE];
	if (argc != 3) {
		printf("Usage : %s <GroupIP> <port> \n", argv[0]);
		exit(1);
	}

	send_sock = socket(PF_INET, SOCK_DGRAM, 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]));

	setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_TTL, (void*)&time_live, sizeof(time_live));
	if ((fp = fopen("news.txt")) == NULL)
		error_handling("fopen() error");

	while (!feof(fp)) /* Broadcasting */
	{
		fgets(buf, BUF_SIZE, fp));
		sendto(send_sock, buf, strlen(buf),
			0, (struct sockaddr*)&mul_adr, sizeof(mul_adr));
		sleep(2);
	}

	close(fp);
	close(send_sock);
	return 0;
}

void error_handling(char* msg)
{
	fputs(msg, stderr);
	fputc('\n', stderr);
	exit(1);
}

1. 멀티캐스트 데이터의 송수신은 UDP 소켓을 대상으로 하기 때문에, 위 소스코드에서는 UDP 소켓을 생성하고 있다.

2. 데이터를 전송할 주소정보를 설정하는데, 반드시 IP주소를 멀티캐스트 주소로 설정해야 한다.

3. setsockopt함수를 통해 소켓의 TTL 정보를 지정하고 있다. Sender에서 반드시 해야 할 일이다.

4. 이후 데이터를 전송하는데 UDP 소켓을 기반으로 데이터 전송이 이뤄지기 때문에 sendto 함수를 사용하는 것은 당연하다. sleep 함수를 호출한 것은 약간의 시간간격을 두기 위함일 뿐 딱히 의미를 갖지는 않는다.

 

 

[news_receiver.c]

/* linux */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define	BUF_SIZE		30
void error_handling(char* msg);

int main(int argc, char* argv[])
{
	int recv_sock;
	int str_len;
	char buf[BUF_SIZE];
	struct sockaddr_in adr;
	struct ip_mreq join_adr;
	if (argc != 3)
	{
		printf("Usage : %s <GroupIP> <port> \n", argv[0]);
		exit(1);
	}

	recv_sock = socket(PF_INET, SOCK_DGRAM, 0);
	memset(&adr, 0, sizeof(adr));
	adr.sin_family = AF_INET;
	adr.sin_addr.s_addr = inet_addr(INADDR_ANY);
	adr.sin_port = htons(atoi(argv[2]));

	if (bind(recv_sock, (struct sockaddr*)&adr, sizeof(adr)) == -1)
		error_handling("bind() error");
	
	join_adr.imr_multiaddr.s_addr = inet_addr(argv[1]);
	join_adr.imr_interface.s_addr = htonl(INADDR_ANY);

	setsockopt(recv_sock, IPPROTO_IP,
		IP_ADD_MEMBERSHIP, (void*)&join_adr, sizeof(join_adr));

	while (1)
	{
		str_len = recvfrom(recv_sock, buf, BUF_SIZE - 1, 0, NULL, 0);
		if (str_len < 0)
			break;
		buf[str_len] = 0;
		fputs(buf, stdout);
	}
	close(recv_sock);
	return 0;
}

void error_handling(char* msg)
{
	fputs(msg, stderr);
	fputc('\n', stderr);
	exit(1);
}

 

 

[실행결과 : news_sender.c]

root@my_linux:/tcpip# gcc news_sender.c -o sender
root@my_linux:/tcpip# ./sender
Usage : ./sender <GroupIP> <PORT>
root@my_linux:/tcpip# ./sender 224.1.1.2 9190

 

[실행결과 : news_receiver.c]

root@my_linux:/tcpip# gcc news_receiver.c -o receiver
root@my_linux:/tcpip# ./receiver
Usage : ./receiver <GroupIP> <PORT>
root@my_linux:/tcpip# ./receiver 224.1.1.2 9190
The 36-year-old had to battle back from a slow start to beat Fognini over 
four sets 2-6 6-4 6-2 6-1 on Thursday night in Arthur Ashe Stadium.
But the victory came with a scary moment in what was to be the final set,
one which left the 22-time grand slam champion bloodied.
As the Spaniard stretched to return a backhand to Fognini, 
Nadal's racket bounced off the court and inadvertently hit him in the face.
Nadal immediately clutched his nose, dropping his racket and forfeiting the point.
He lay down on the court by the umpire's chair and moved his hand away from his nose, 
showing the blood which had begun to flow.
He needed a medical timeout to receive attention for the injury, 
later returning to close out the set. After the victory, 
Nadal played down the injury, saying he felt a "little bit pain, honestly, but good."

실행 시 지극히 당연한 이야기이지만  Sender와 Receiver 사이의 PORT번호는 당연히 일치해야 하며, 실행 순서는 중요하지 않다. TCP 소켓처럼 연결된 상태에서 데이터를 송수신하는 것이 아니기 때문이다. 다만 멀티캐스트는 어디까지나 방송의 개념이기 때문에 Receiver를 늦게 실행하면, 그 이전에 전송된 멀티캐스트 데이터는 수신이 불가능하다.

 

 

[MBone(Multicast Backbone]

멀티캐스트는 MBone이라 불리는 가상 네트워크를 기반으로 동작한다. 일단 가상 네트워크라는 표현이 생소하게 들리는데, 이는 인터넷상에서 별도의 프로토콜을 기반으로 동작하는, 소프트웨어적인 개념의 네트워크로 이해할 수 있다. 즉, MBone은 손으로 만질 수 있는 물리적인 개념의 네트워크가 아니다. 멀티캐스트에 필요한 네트워크 구조를 인터넷 망을 바탕으로 소프트웨어적으로 구현해 놓은 가상의 네트워크이다. 멀티캐스트가 가능하도록 돕는 가상 네트워크 연구는 지금도 계속되고 있으며, 이는 멀티캐스트 기반의 응용 소프트웨어를 개발하는 것과는 다른 분야의 연구이다.

 

 

#2. 브로드캐스트(Broadcast)

브로드캐스트는 한번에 여러 호스트에게 데이터를 전송한다는 점에서 멀티캐스트와 유사하다. 그러나 전송이 이뤄지는 범위에서 차이가 난다. 멀티캐스트는 서로 다른 네트워크상에 존재하는 호스트라 할지라도, 멀티캐스트 그룹에 가입만 되어 있으면 데이터의 수신이 가능하다. 반면, 브로드캐스트는 동일한 네트워크로 연결되어 있는 호스트로, 데이터의 전송 대상이 제한된다.

 

브로드캐스트의 이해와 구현방법

브로드캐스트는 동일한 네트워크에 연결되어 있는 모든 호스트에게 동시에 데이터를 전송하기 위한 방법이다. 이 역시 멀티캐스트와 마찬가지로 UDP를 기반으로 데이터를 송수신한다. 그리고 데이터 전송 시 사용되는 IP주소의 형태에 따라서 다음과 같이 두 가지 형태로 구분이 된다.

- Directed 브로드캐스트(Broadcast)

- Local 브로드캐스트(Broadcast)

 

코드상에서 이 둘의 차이점은 IP주소에 있다. Directed 브로드캐스트의 IP주소는 네트워크 주소를 제외한 나머지 호스트 주소를 전부 1로 설정해서 얻을 수 있다. 예를 들어서 네트워크 주소가 192.12.34인 네트워크에 연결되어 있는 모든 호스트에게 데이터를 전송하려면 192.12.34.255로 데이터를 전송하면 된다. 이렇듯 특정 지역의 네트워크에 연결된 모든 호스트에게 데이터를 전송하려면 Directed 브로드캐스트 방식으로 데이터를 전송하면 된다.

반면 Local 브로드캐스트를 위해서는 255.255.255.255라는 IP주소가 특별히 예약되어 있다. 예를 들어서 네트워크 주소가 192.32.24인 네트워크에 연결되어 있는 호스트가 IP주소 255.255.255.255를 대상으로 데이터를 전송하면, 192.32.24로 시작하는 IP주소의 모든 호스트에게 데이터가 전달된다.

그렇다면 브로드캐스트 Sender와 Receiver는 어떻게 구현해야 할까? 사실 브로드캐스트 예제는 데이터 송수신에 사용되는 IP주소를 유심히 관찰하지 않으면, UDP 예제와 잘 구분이 안 된다. 즉, 데이터 송수신에 사용되는 IP주소가 UDP 예제와의 유일한 차이점이다. 다만 기본적으로 생성되는 소켓은 브로드캐스트 기반의 데이터 전송이 불가능하도록 설정되어 있기반의 데이터 전송이 불가능하도록 설정되어 있기 때문에 다음 유형의 코드 구성을 통해서 이를 변경할 필요는 있다.

int send_sock;
int bcast = 1; // SO_BROADCAST의 옵션정보를 1로 변경하기 위한 변수 초기화
.....
send_sock = socket(PF_INET, SOCK_DGRAM, 0);
.....
setsockopt(send_sock, SOL_SOCKET, SO_BROADCAST, (void*)&bcast, sizeof(bcast));
.....

위의 setsockopt 함수호출을 통해서 SO_BROADCAST의 옵션정보를 변수 bcast에 저장된 값인 1로 변경하는데, 이는 브로드캐스트 기반의 데이터 전송이 가능함을 의미한다. 물론 위에서 보인 소켓옵션의 변경은 데이터를 전송하는 Sender에나 필요할 뿐, Receiver의 구현에서는 필요가 없다.

 

브로드캐스트 기반의 Sender와 Receiver의 구현

[news_sender_brd.c]

/* linux */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define BUF_SIZE			30
void error_handling(char* msg);

int main(int argc, char* argv[])
{
	int send_sock;
	struct sockaddr_in broad_adr;
	FILE* fp;
	char buf[BUF_SIZE];
	int so_brd = 1;
	if (argc != 3)
	{
		printf("Usage : %s <Broadcast IP> <PORT> \n", argv[0]);
		exit(1);
	}

	send_sock = socket(PF_INET, SOCK_DGREAM, 0);
	memset(&broad_adr, 0, sizeof(broad_adr));
	broad_adr.sin_family = AF_INET;
	broad_adr.sin_addr.s_addr = inet_addr(argv[1]);
	broad_adr.sin_port = htons(atoi(argv[2]));

	setsockopt(send_sock, SOL_SOCKET, SO_BRODCAST, (void*)&so_brd, sizeof(so_brd));
	if ((fp = fopen("news.txt", "r")) == NULL)
		error_handling("fopen() error")s;

	while (!feof(fp))
	{
		fgets(buf, BUF_SIZE, fp);
		sendto(send_sock, buf, strlen(buf), 0, (struct sockaddr*)&broad_adr, sizeof(broad_adr));
		sleep(2);
	}

	close(fp);
	close(send_sock);
	return 0;
}


void error_handling(char* msg)
{
	fputs(msg, stderr);
	fputc('\n', stderr);
	exit(1);
}

UDP 소켓을 기반으로 브로드캐스트 기반 데이터 전송이 가능하도록 옵션정보를 변경하고 있다. 이를 제외하면 일반적인 UDP Sender와 차이가 나지 않는다.

 

[news_receiver_brd.c]

/* linux */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define BUF_SIZE		30	
void error_handling(char* msg);

int main(int argc, char* argv[])
{
	int recv_sock;
	struct sockaddr_in adr;
	int str_len;
	char buf[BUF_SIZE];

	if (argc != 2)
	{
		printf("Usage : %s <PORT> \n", argv[0]);
		exit(1);
	}

	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[1]));

	if (bind(recv_sock, (struct sockaddr*)&adr, sizeof(adr)) == -1)
		error_handling("bind() error");
	while (1)
	{
		str_len = recvfrom(recv_sock, buf, BUF_SIZE - 1, 0, NULL, 0);
		if (str_len < 0)
			break;
		fputs(buf);
	}
	close(recv_sock);
	return 0;
}


void error_handling(char* msg)
{
	fputs(msg, stderr);
	fputc('\n', stderr);
	exit(1);
}

소스코드상에서 봐도 UDP 소켓통신과 다를게 없어 보인다.

 

[실행결과 : news_sender_brd.c]

root@my_linux:/tcpip# gcc news_sender_brd.c -o sender
root@my_linux:/tcpip# ./sender 255.255.255.255 9190

 

[실행결과 : news_receiver_brd.c]

root@my_linux:/tcpip# gcc news_receiver_brd.c -o receiver
root@my_linux:/tcpip# ./receiver 9190
The 36-year-old had to battle back from a slow start to beat Fognini over 
four sets 2-6 6-4 6-2 6-1 on Thursday night in Arthur Ashe Stadium.
But the victory came with a scary moment in what was to be the final set,
one which left the 22-time grand slam champion bloodied.
As the Spaniard stretched to return a backhand to Fognini, 
Nadal's racket bounced off the court and inadvertently hit him in the face.
Nadal immediately clutched his nose, dropping his racket and forfeiting the point.
He lay down on the court by the umpire's chair and moved his hand away from his nose, 
showing the blood which had begun to flow.
He needed a medical timeout to receive attention for the injury, 
later returning to close out the set. After the victory, 
Nadal played down the injury, saying he felt a "little bit pain, honestly, but good."

 

 

#3. 윈도우 기반으로 구현하기

리눅스와 별차이는 없고, 헤더파일의 선언부분만 조금 다르다.

[news_sender_win.c]

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <WinSock2.h>
#include <WS2tcpip.h>

#pragma comment(lib, "ws2_32.lib")

#define TTL			64
#define BUF_SIZE	30

void ErrorHandling(const char* msg);

int main(int argc, char* argv[])
{
	WSADATA wsaData;
	SOCKET hSendSock;
	SOCKADDR_IN mulAdr;
	int timeVal = TTL;
	FILE* fp;
	char buf[BUF_SIZE];


	if (argc != 3)
	{
		printf("Usage : %s <GroupIP> <PORT> \n", argv[0]);
		exit(1);
	}

	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
	{
		ErrorHandling("WSAStartup() error");
	}

	hSendSock = socket(PF_INET, SOCK_DGRAM, 0);
	if (hSendSock == INVALID_SOCKET)
	{
		ErrorHandling("socket() error");
	}
	memset(&mulAdr, 0, sizeof(mulAdr));
	mulAdr.sin_family = AF_INET;
	mulAdr.sin_addr.S_un.S_addr = inet_addr(argv[1]);
	mulAdr.sin_port = htons(atoi(argv[2]));

	setsockopt(hSendSock, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&timeVal, sizeof(timeVal));
	if ((fp = fopen("news.txt", "r")) == NULL)
	{
		ErrorHandling("fopen() error");
	}
	
	while (!feof(fp))
	{
		fgets(buf, BUF_SIZE, fp);
		sendto(hSendSock, buf, strlen(buf), 0, (SOCKADDR*)&mulAdr, sizeof(mulAdr));
		Sleep(2000);
	}
	fclose(fp);
	closesocket(hSendSock);
	WSACleanup();
	return 0;
}

void ErrorHandling(const char* msg)
{
	fputs(msg, stderr);
	fputc('\n', stderr);
	exit(1);
}

 

[news_receiver_win.c]

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <WinSock2.h>
#include <WS2tcpip.h>

#pragma comment(lib, "ws2_32.lib")

#define BUF_SIZE		30
void ErrorHandling(const char* msg);

int main(int argc, char* argv[])
{
	WSADATA wsaData;
	SOCKET hRecvSock;
	SOCKADDR_IN adr;
	struct ip_mreq joinAdr;
	char buf[BUF_SIZE];
	int strLen;

	if (argc != 3)
	{
		printf("Usage : %s <GroupIP> <PORT> \n", argv[0]);
		exit(1);
	}

	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
	{
		ErrorHandling("WSAStartup() error");
	}

	hRecvSock = socket(PF_INET, SOCK_DGRAM, 0);
	if (hRecvSock == INVALID_SOCKET)
	{
		ErrorHandling("socket() error");
	}
	memset(&adr, 0, sizeof(adr));
	adr.sin_family = AF_INET;
	adr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	adr.sin_port = htons(atoi(argv[2]));
	if (bind(hRecvSock, (SOCKADDR*)&adr, sizeof(adr)) == SOCKET_ERROR)
	{
		ErrorHandling("bind() error");
	}

	joinAdr.imr_multiaddr.S_un.S_addr = inet_addr(argv[1]);
	joinAdr.imr_interface.S_un.S_addr = htonl(INADDR_ANY);
	if (setsockopt(hRecvSock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
		(const char*)&joinAdr, sizeof(joinAdr)) == SOCKET_ERROR)
	{
		ErrorHandling("setsockopt() error");
	}

	while (1)
	{
		strLen = recvfrom(hRecvSock, buf, BUF_SIZE - 1, 0, NULL, 0);
		if (strLen < 0)
			break;
		buf[strLen] = 0;
		fputs(buf, stdout);
	}

	closesocket(hRecvSock);
	WSACleanup();
	return 0;
}

void ErrorHandling(const char* msg)
{
	fputs(msg, stderr);
	fputc('\n', stderr);
	exit(1);
}

(멀티캐스트 IP 주소 대역은 224.0.0.0 ~ 239.225.225.255 이다. D클래스임)

 

 

[내용확인문제]

01. TTL이 의미하는 바는 무엇인가? 그리고 TTL의 값이 크게 설정되는 것과 작게 설정되는 것에 따른 차이와 문제점을 라우팅의 관점에서 설명해보자.

답)

TTL은 Time to live의 약자로 패킷을 얼마나 멀리 전달할 것인지를 결정하는 요소이다. TTL은 라우터를 1나씩 지날때마다 1씩 감소하며, 너무 크게 설정하면 네트워크 트래픽에 좋지 못한 영향을 주고, 너무 적게 설정해도 목적지에 도달하지 않는 문제가 발생한다.

 

02. 멀티캐스트와 브로드캐스트의 공통점은 무엇이고 또 차이점은 무엇인가? 데이터의 송수신 관점에서 설명해보자.

답)

멀티캐스트와 브로드캐스트는 하나의 sender가 둘 이상의 receiver들에게 일괄적으로 데이터를 송신한다는 공통점이 있다. 그러나 두 캐스트는 전송 범위에 있어서 차이가 있다. 멀티캐스트는 네트워크가 다르더라도 동일 그룹(IP)에 속한(가입한) 호스트에게 데이터 송신이 가능하지만, 브로드캐스트는 동일한 네트워크에 속한 호스트에게만 데이터 송신이 가능하다. 

 

03. 다음 중 멀티캐스트에 대한 설명으로 옳지 않은 것을 모두 고르면?

a. 멀티캐스트는 멀티캐스트 그룹에 가입한 모든 호스트에게 데이터를 전송하는데 사용되는 프로토콜이다.

b. 멀티캐스트 그룹에 가입하기 위해서는 동일 네트워크에 연결되어 있어야 한다. 즉, 둘 이상의 네트워크에 걸쳐서 하나의 멀티캐스트 그룹이 형성될 수 없다.

c. 멀티캐스트 그룹에 가입할 수 있는 호스트의 수에는 제한이 없으나, 이 그룹으로 데이터를 전송하는 호스트(Sender)의 수는 하나로 제한이 된다.

d. 멀티캐스트를 위한 소켓은 UDP 소켓이어야 한다. 멀티캐스트는 UDP를 기반으로 데이터를 송수신하기 때문이다.

답)

b. 기본적으로 멀티캐스트는 MBone이라 불리는 가상 네트워크를 기반으로 동작한다. 기존의 인터넷 망 위에 소프트웨어적으로 구현한 네트워크 이므로 동일한 그룹에 가입된 모든 호스트는 데이터 수신을 받을 수 있다. 따라서 네트워크의 종류와는 아무 연관이 없다.

c. sender(호스트)의 수 역시 제한이 없다.

 

04. 멀티캐스트는 트래픽 측면에서도 긍정적이다! 그렇다면 어떠한 이유로, 어떻게 긍정적인지 TCP의 데이터 송수신 방식과 비교해서 설명해보자.

답)

TCP의 데이터송수신은 연결을 기반으로 송수신한다. 그러나 멀티캐스트는 방송의 개념이기 때문에 UDP를 기반으로 송수신하므로 데이터 송신을 한번만 진행하면 된다. 즉  호스트수가 아무리 많더라도 결국 sender(호스트)측에서 보내는 패킷은 1번이다. 그런 측면에서 긍정적이다.(UDP의 특징) 또한 멀티캐스트 데이터는 라우터상에서 하나의 데이터가 여러개로 복사가 된다.

 

05. 멀티캐스트 방식의 데이터 송수신을 위해서는 MBone(Multicast Backbone)이라는 가상의 네트워크가 구성되어 있어야 한다. 즉, MBone은 멀티캐스트를 위한 네트워크이다. 그런데 이러한 MBone을 가리켜 가상 네트워크라 한다. 그렇다면 여기서 말하는 가상 네트워크가 무엇을 뜻하는지 설명해보자.

답)

물리적인 네트워크인 인터넷 망위에 소프트웨어적으로 구현해 놓은 네트워크이다. 그래서 가상의 네트워크라고도 부른다.