Part1_Multicasting & Broadcasting

·2023년 11월 30일
0

[멀티캐스트]

멀티캐스트 방식은 UDP를 기반으로 하는 전송 방식임
둘 이상의 호스트에게 데이터를 전송하고자 하는 경우에는 데이터를 두 번 전송해야 함
그러나 멀티캐스트 방식에서는 데이터 전송의 목적지가 하나의 호스트가 아니라 멀티캐스트 그룹에
속해 있는 모든 호스트들이 목적지가 됨
→ 단 한번의 데이터 전송으로 여러 클라이언트들에게 데이터를 전달할 수 있게 됨

[전송 방식]

멀티캐스트 패킷을 수신하기 위해서는 멀티캐스트 그룹에 가입을 해야 함
멀티캐스트 패킷이란 받는 사람의 주소 정보가 클래스 D의 IP 주소 중 하나로 설정되어 있는 UDP 패킷을 의미

[라우팅과 TTL]

멀티캐스트 패킷을 전송하기 위해서는 TTL 설정 과정을 반드시 거쳐야 함
TTL은 TimeToLive의 약자로 “얼마나 멀리 전달될 것인가?”를 결정하는 주요 요소임
TTL은 정수로 표현되며, 라우터를 거쳐갈 때마다 하나씩 감소하게 되며
0이 되면 패킷은 더 이상 전달되지 못하고 소멸됨
→ TTL 값을 너무 크게 주면 네트워크의 트래픽에 좋지 못한 영향을 주게 되며
너무 적게 설정해도 목적지에 도달하지 못하는 문제를 가져올 수 있음

[멀티캐스트 서버 클라이언트]

멀티캐스트 그룹으로 패킷을 전송하는 호스트가 Sender가 되고,
멀티캐스트 그룹에 가입을 해서 패킷 수신을 기대하고 있는 호스트가 Receiver가 됨
Receiver는 데이터를 수신하기 위해 가입(join)의 절차를 거쳐야 하지만
Sender는 UDP 소켓을 생성하고 멀티캐스트 주소로 데이터를 전송해 주기만 하면 됨
TTL도 반드시 설정해 줘야 함
(TTL 설정 없이 멀티캐스트 주소로 데이터를 전송하면 기본적으로 TTL은 1이기 때문에 외부로 패킷 전송 불가)

[sender 구현]

// news_sender.c

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

#define TTL 64
#define BUFSIZE 30

void error_handling(char* message);

int main(int argc, char** argv)
{
	int send_sock;
	struct sockaddr_in multi_addr;
	int multi_TTL = TTL;
	int state;
	FILE* fp;
	char buf[BUFSIZE];

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

	send_sock = socket(PF_INET, SOCK_DGRAM, 0); // UDP 소켓 생성
	if(send_sock == -1)
		error_handling("socket() error");

	// 데이터를 전송할 주소 정보 설정
	memset(&multi_addr, 0, sizeof(multi_addr));
	multi_addr.sin_family = AF_INET;
	multi_addr.sin_addr.s_addr = inet_addr(argv[1]);
	multi_addr.sin_port = htons(atoi(argv[2]));

	// 소켓의 디폴트 TTL 변경
	state = setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_TTL, (void*)&multi_TTL, sizeof(multi_TTL));

	if(state)
		error_handling("setsockopt() error");

	if((fp = fopen("News.txt", "r")) == NULL)
		error_handling("fopen() error");

	while(!feof(fp))
	{
		fgets(buf, BUFSIZE, fp);
		// 데이터 전송
		// 전송 방식이 UDP이므로 sendto 사용
		sendto(send_sock, buf, strlen(buf), 0, (struct sockaddr*)&multi_addr, sizeof(multi_addr));
		sleep(2);
	}

	close(send_sock);
	return 0;
}

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

[receiver 구현]

// news_receiver.c

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

#define BUFSIZE 30
void error_handling(char* message);

int main(int argc, char** argv)
{
	int recv_sock;
	struct sockaddr_in addr;
	int state, str_len;
	char buf[BUFSIZE];
	struct ip_mreq join_addr; // ip_mreq 구조체 변수를 선언 

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

	recv_sock = socket(PF_INET, SOCK_DGRAM, 0);
	if(recv_sock == -1)
		error_handling("socket() error");

	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = htonl(INADDR_ANY);
	addr.sin_port = htons(atoi(argv[2]));

	if(bind(recv_sock, (struct sockaddr*)&addr, sizeof(addr)) == -1)
		error_handling("bind() error");

	// join_addr 초기화
	join_addr.imr_multiaddr.s_addr = inet_addr(argv[1]);
	join_addr.imr_interface.s_addr = htonl(INADDR_ANY);

	// IP_ADD_MEMBERSHIP 옵션 설정을 통해 멀티캐스트 그룹에 가입을 하고 있음
	state = setsockopt(recv_sock, IPROTO_IP, IP_ADD_MEMBERSHIP, (void*)&join_addr, sizeof(join_addr));

	if(state)
		error_handling("setsockopt() error");

	while(1) // 데이터 수신
	{
		str_len = recvfrom(recv_sock, buf, BUFSIZE - 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);
}

[ip_mreq 구조체]

struct ip_mreq
{
	struct in_addr imr_multiaddr; // IPv4 class D multicast addr : 가입할 멀티캐스트 그룹의 주소 명시
	struct in_addr imr_interface; // Ipv4 addr of local interface : 가입하는 호스트의 주소 정보 명시
																// INADDR_ANY도 가능
}

인자 전달 시 멀티캐스트 주소를 전달해야 함

[브로드캐스트]

동일한 네트워크로 연결되어 있는 모든 호스트들에게 데이터를 전송해 주기 위해서 사용됨
기본적으로 UDP 방식 사용
브로드캐스트 주소는 네트워크 IP를 제외한 나머지 호스트 IP를 전부 1로 설정하면 얻을 수 잇음
(예를 들어 192.12.34로 시작하는 클래스 C 네트워크 주소의 경우 192.12.34.255와 같이 설정하면 됨)
패킷을 전송하는 호스트가 속해 있는 네트워크에 연결되어 있는 모든 호스트들에게 패킷을 전달하는 경우
255.255.255.255라는 브로드캐스트 주소를 사용할 수 있음 (지역적 브로드캐스트)

[브로드캐스트 서버 클라이언트]

패킷이 전송되는 주소를 브로드캐스트 주소로 지정해주고,
브로드캐스트를 위애 생성한 소켓이 브로드캐스트가 가능하도록 설정해 주기만 하면 됨
(디폴트로 생성되는 UDP 소켓은 브로드캐스트가 불가능하도록 초기화 되어있기 때문에 설정이 필요함)

[sender]

// news_sender_broad.c

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

#define BUFSIZE 30
#define TRUE 1
#define FALSE 0

void error_handling(char* message);

int main(int argc, char** argv)
{
	int send_sock;
	struct sockaddr_in broad_addr;
	int state;
	FILE* fp;
	char buf[BUFSIZE];
	int so_broadcast = TRUE;

	if(argc != 3)
	{
		printf("Usage : %s <Broadcast IP> <port>\n", argv[0]);
		exit(1);
	}
	
	// UDP 소켓 생성
	send_sock = socket(PF_INET, SOCK_DGRAM, 0);
	if(send_sock == -1)
		error_handling("socket() error");

	memset(&broad_addr, 0, sizeof(broad_addr));
	broad_addr.sin_family = AF_INET;
	broad_addr.sin_addr.s_addr = inet_addr(argv[1]);
	broad_addr.sin_port = htons(atoi(argv[2]));

	// UDP 소켓의 옵션 변경 (브로드캐스트가 가능하도록)
	state = setsockopt(send_sock, SOL_SOCKET, SO_BROADCAST, (void*)&so_broadcast, sizeof(so_broadcast));

	if(state)
		error_handling("setsockopt() error");

	if((fp = fopen("News.txt", "r")) == NULL)
		error_handling("fopen() error");

	while(!feof(fp))
	{
		fgets(buf, BUFSIZE, fp);
		// 데이터 전송
		sendto(send_sock, buf, strlen(buf), 0, (struct sockaddr*)&broad_addr, sizeof(broad_addr));

		sleep(2);
	}
	
	close(send_sock);
	return 0;
}

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

[receiver]

브로드캐스트 Receiver는 bind 함수의 호출을 필요로 함
브로드캐스트 주소로 패킷이 전송되었다 하더라도 Port는 일치시켜 줘야 패킷을 수신할 수 있기 때문
즉 Sender가 Port 9190으로 브로드캐스트 했을 경우, Receiver가 패킷을 수신하기 위해서는 소켓의 Port 정보를
9190으로 미리 맞춰 놓고 기다리고 있어야 함
→ 그러기 위해 bind 함수의 호출이 선행되어야 함

// news_receiver_broad.c

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

#define BUFSIZE 30
void error_handling(char* message);

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

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

	recv_sock = socket(PF_INET, SOCK_DGRAM, 0);
	if(recv_sock == -1)
		error_handling("socket() error");

	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = htonl(INADDR_ANY);
	addr.sin_port = htons(atoi(argv[1]));

	// 소켓에 Port 할당
	// 이제 여기서 할당된 Port로 전달되는 브로드캐스트 패킷은 모두 수신 가능
	if(bind(recv_sock, (struct sockaddr*)&addr, sizeof(addr)) == -1)
		error_handling("bind() error");

	while(1)
	{
		str_len = recvfrom(recv_sock, buf, BUFSIZE - 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);
}

0개의 댓글