본문 바로가기
책/TCPIP 소켓프로그래밍

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

by 맑은청이 2020. 5. 4.
728x90
반응형
int send_sock;
int time_lise =64;

.....

send_sock=socket(PF_INET,SOCK_DGRAM,0);
setsockopt(send_sock,IPPROTO_IPIP_MULTICAST_TTL,(void*)&time_live,sizeof(time_live));

오늘은 멀티캐스트와 브로드캐스트에 대해 배워보겠습니다.  멀티캐스트와 브로드캐스트, 어디서 많이 들어본 거 같은 단어들입니다. 방송에서 많이 들은 거 같습니다. 여러분이 만약 방송사를 운영하고 있다면 1000명의 클라이언트가 서비스 요청을 할때 TCP기반이면 1000개의 소켓 연결을 유지해야하고 UDP 로 한다고 하더라도 1000회의 데이터 전송이 필요합니다. 이렇듯 다수의 클라이언트에게 동일한 데이터를 전송하는 것은 서버와 네트워크 트래픽 측면에서 매우 부정적입니다.  그렇기 때문에 멀티캐스트라는 기술이 존재하는 겁니다. 

 

멀티캐스트 방식의 데이터 전송은 UDP 기반으로 합니다. 그러므로 구현 방식이 UDP 와 매우 유사합니다. 차이점은 UDP 는 하나의 목적지를 두지만 멀티캐스트는 데이터 전송이 특정 그룹에 가입되어 있는 다수의 호스트라는 점입니다. 즉 멀티캐스트는 한번의 전송으로 다수의 호스트에게 데이터를 전송할 수 있습니다. 멀티캐스트는 라우터의 도움으로 UDP 패킷을 하나만 띄웁니다. 라우터가 이를 복사해주기 때문입니다. 

 

 

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

 1) 서버가 멀티 캐스트 그룹을 대상을 데이터를 딱 한 번 전송

 2) 딱 한번 전송해도 그룹에 클라이언트 모두 데이터 수신 가능 

 3) 그룹의 수는 IP 주소 범위 내에서 얼마든지 추가 가능 

 4) 이 그룹에 가입하면 데이터 수신 가능

 

여기서 말한 멀티캐스트 그룹이란 클래스 D 에 속하는 IP주소(224.0.0.0 ~ 239.255.255.255) 를 의미합니다. 따라서 그룹의 가입은 '내가 이 목적지로 전송되는 데이터가 받고 싶으니 수신하겠다~' 라는 의미입니다. ( 그렇게 대단하지 않습니다.)

멀티캐스트 라우팅

 

다음과 같이 그룹 AAA로 전송된 하나의 멀티캐스트 패킷이 라우터들의 도움으로 모든 호스트에게 전달되고 있습니다. 

여기서 의문이 듭니다. '트래픽 측면으로 보니 별로일거 같은데!' 하지만 생각해보면 트래픽 문제 때문에 멀티캐스트기술이 존재한다고 했습니다. 그림으로만 보면 트래픽이 부정적이라고 생각할 수 있습니다. 하나의 패킷이 여러 라우터를 통해서 빈번히 복사되기 때문입니다. 단순히 생각하면 그렇습니다.

 

하지만 '하나의 영역에 동일한 패킷이 둘 이상 전송되지 않는다!' 라는 측면을 봅시다. 

 

만약 TCP,UDP 방식으로 1000개의 호스트에 파일을 전송하면 1,000회 파일을 전송해야합니다. 열개의 호스트가 묶여있어서 경로가 거의 일치해도 말입니다. 하지만 멀티캐스트 방식으로 파일을 전송하면 딱 한번만 전송해주면 됩니다. 라우터가 1000개를 복사해줄테니 말입니다. 이러한 성격 때문에 멀티캐스트는 '멀티미디어 데이터의 실시간 전송'에 주로 이용됩니다. 

 

 

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

멀티캐스트 패킷의 전송을 위해서는 TTL이라는 것의 설정과정을 반드시 거쳐야합니다. TTL이란 '패킷을 얼마나 멀리 전달할 것인가' 를 결정하는 주요소가 됩니다. TTL은 정수로 표현되고 라우터를 하나씩 지날 때마다 -1이 됩니다. 이 값이 0 이 되면 패킷은 소멸됩니다. TTL을 너무 크게 설정하면 네트워크 트래픽에 좋지 못한 영향을 주고 너무 적게 설정하면 목적지에 도착할 수 없기 때문에 주의하여야 합니다. 

 

TTL설정 방법에 대해 알아보겠습니다. 말하자면 '소켓의 옵션설정'을 통해 이루어집니다. 이와 관련된

프로토콜의 레벨은 IPPROTO_IP이고

옵션의 이름은 IP_MULTICATST_TTL 입니다.

 

64로 TTL을 설정하고 싶다면 다음 코드를 구성하면 됩니다.

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 iq_mreq{
	struct in_addr imr_multiaddr;//가입할 그룹의 IP주소
    struct in_addr imr_interface;//그룹에 가입하는 소켓이 속한 호스트의 IP주소 명시, INADDR_ANY도 가능
    }

 

멀티캐스트 sender 와 receiver 구현코드 첨부합니다. 

sender 보단 receiver가 조금 더 복잡합니다. receiver는 그룹의 가입을 해야하기 때문입니다. 

 

//sender.c
#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 *message);

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);	//멀티캐스트 데이터 송수신은 UDP 소켓을 대상으로 합니다.
	memset(&mul_adr,0,sizeof(mul_adr));
    
    //데이터를 전송할 주소정보를 설정하고 있습니다. 중요한건 반드시 IP 주소를 멀티캐스트 주소로 설정해야합니다. 
	mul_adr.sin_family=AF_INET;
	mul_adr.sin_addr.s_addr=inet_addr(argv[1]);
	mul_adr.sin_port=htons(atoi(argv[2]));

	//소켓의 TTL정보를 지정하고 있습니다. Sender의 필수 과정입니다. 
	setsockopt(send_sock,IPPROTO_IP,IP_MULTICAST_TTL,(void*)&time_live,sizeof(time_live));
	if((fp=fopen("news.txt","r"))==NULL)
		error_handling("fopen() error");

	//실제 데이터 전송이 이루어지는 영역입니다. UDP 소켓임으로 sendto를 사용해야합니다. 
	while(!feof(fp)){
		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* message){
	fputs(message,stderr);
	fputc('\n',stderr);
	exit(1);
}	
#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 *message);

int main(int argc,char *argv[]){
	int recv_sock;
	int str_len;
	char buf[BUF_SIZE];
	struct sockaddr_in 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=htonl(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]);//멀티캐스트 그룹 IP 주소
	join_adr.imr_interface.s_addr=htonl(INADDR_ANY);//그룹에 가입할 호스트 IP 주소
	
    //그룹가입
	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* message){
        fputs(message,stderr);
        fputc('\n',stderr);
}

 

 

 

-MBone

멀티캐스트는 MBone 이라는 가상네트워크를 기반으로 동작합니다. 가상네트워크란 인터넷상에서 별도의 프로토콜을 기반으로 동작하는 소프트웨어적인 개념의 네트워크라고 이해하면 됩니다. 즉 물리적인 개념의 네트워크가 아닙니다. 멀티캐스트에 필요한 네트워크 구조를 인터넷 망을 바탕으로 소프트웨어적으로 구현해 놓은 가상의 네트워크입니다. 

 

 

 

-브로드캐스트(Broadcast)

브로드캐스트를 설명해보겠습니다. 브로드캐스트는 한번에 여러 호스트에게 데이터를 전송한다는 점에서 멀티캐스트와 유사하지만 전송 범위에서 차이가 납니다. 멀티캐스트는 서로 다른 네트워크 상에서 존재하는 호스트끼리도 그룹에 가입되면 데이터 수신이 가능하지만 브로드캐스트는 동일한 네트워크로 연결되어 있는 호스트로 전송대상이 제한됩니다. 

 

 

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

역시 멀티캐스트와 마찬가지로 UDP 기반으로 데이터를 송수신합니다. 

그리고 데이터 전송 시 사용되는 IP 주소의 형태에 따라 

  1.Directed Broadcast

  2.Local Broadcast

로 구분됩니다. 

 

둘의 차이는 IP 주소입니다. 

 

Directed Broadcast의 IP주소는 네트워크 주소를 제외한 나머지 호스트 주소를 전부 1로 설정해서 얻을 수 있습니다. 

예를 들어 192.12.34 네트워크에 연결되어 있는 모든 호스트에게 데이터를 전송할려면 192.12.34.255로 데이터를 전송하면 됩니다. 이렇듯 특정 지역의 네트워크에 연결된 호스트에게 데이터를 전송하려면 이 방법을 사용합니다. 

 

Local Broadcast는 255.255.255.255라는 IP 주소가 특별히 예약되어 있습니다. 예를 들면 192.32.24 인 네트워크에 연결되어 있는 호스트가 255.255.255.255를 대상으로 데이터 전송하면 192.32.24로 시작하는 IP 주소의 모든 호스트에게 데이터가 전달됩니다. 

 

사실 브로드 캐스트는 IP 주소를 제외하곤 UDP 예제와 동일합니다. 다만 기본적으로 생성되는 소켓은 브로드캐스트 기반의 데이터 전송이 불가능하도록 설정되어 있기 때문에 아래코드처럼 변경해야합니다. 

int send_sock;
int bcast = 1; 
send_sock=socket(PF_INET,SOCK_DGRAM,0);
...
setsockopt(send_sock,SOL_SOCKET,SO_BROADCAST,(void*)&bcast,sizeof(bcast));
...

 

구현코드를 첨부하겠습니다. 

//브로드캐스트 sender
#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 *message);

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 <Boradcast IP> <PORT>\n", argv[0]);
		exit(1);
	}
  
	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]);
	broad_adr.sin_port=htons(atoi(argv[2]));
	
	setsockopt(send_sock, SOL_SOCKET, 
		SO_BROADCAST, (void*)&so_brd, sizeof(so_brd));	
        //UDP소켓을 브로드캐스트 기반 데이터 전송이 가능하도록 옵션 정보를 변경합니다.
        
	if((fp=fopen("news.txt", "r"))==NULL)
		error_handling("fopen() error");

	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(send_sock);
	return 0;
}

void error_handling(char *message)
{
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}
//receiver 입니다. 
#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 *message);

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;
		buf[str_len]=0;
		fputs(buf, stdout);
	}
	
	close(recv_sock);
	return 0;
}

void error_handling(char *message)
{
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}
728x90
반응형