출처 : 열혈 TCP/IP 소켓 프로그래밍 윤성우 저
멀티 캐스트 방식의 데이터 전송
UDP 기반 데이터 전송으로 특정 멀티 캐스트용 IP 그룹에 속해있는 다수의 호스트로 데이터를 전송할 수 있다. 멀티 캐스트 방식을 이용하면 단 한번에 데이터 전송으로 다수의 호스트에게 데이터를 전송할 수 있다.
멀티캐스트의 데이터 전송방식과 멀티캐스트 트래픽 이점
- 멀티캐스트 서버는 특정 멀티캐스트 그룹을 대상으로 데이터를 딱 한번 전송한다.
- 한번 전송하더라도 그룹에 속하는 클라이언트는 모두 데이터를 수신한다.
- 멀티캐스트 그룹의 수는 IP 주소 범위 내에서 얼마든지 추가 가능하다.
- 특정 멀티캐스트 그룹으로 전송되는 데이터를 수신하려면 해당 그룹에 가입하면 된다.
멀티캐스트 그룹이란 IP주소의 D 클래스 범주에 속하는 주소를 말한다.
멀티캐스트 방식으로 전송한 데이터는 해당 주소 영역에서 라우터가 데이터를 복사하여 모든 그룹 내 호스트에게 전송해준다. 기존 TCP와 UDP 방식은 호스트의 수만큼 전송을 해야하지만, 멀티 캐스트의 경우 한 번만 전송하면 된다.
라우팅과 TTL(Time to Live)
TTL이란 Time to Live의 약자로 패킷을 얼마나 멀리 보낼지를 결정한다. 패킷을 보낼 때 기본 정수만큼 설정하고 패킷이 라우터를 하나 거칠때 마다 1씩 감소한다. 이 값이 0이되면 더 이상 전송되지않고 소멸한다.
- 송신자 측
TTL 설정
소켓 옵션의 IP_MULTICAST_TTL과 함께 TTL 값을 전달하여 설정한다.
int send_sock = socket(PF_INET, SOCK_DGRAM, 0);
setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_TTL, (void*)&time_live, sizeof(time_live));
- 수신자 측
수신자는 멀티캐스트 그룹에 가입하여 해당 멀티캐스트 IP 주소로 송신되는 데이터를 받을 수 있도록 해야한다.
int recv_sock;
struct ip_mreq join_adr;
//...
int 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주소를 명시한다.
멀티캐스트 Sender와 Receiver 구현
송신자가 멀티캐스티 IP를 통해 파일을 전송하는 예시를 보자.
송신자 코드
int main(int argc, char*argv[])
{
int send_sock = socket(PF_INET, SOCK_DGRAM, 0); // UDP Protocol
struct sockaddr_in mul_adr;
memset(&mul_adr, 0, sizeof(mul_adr));
mul_adr.sin_family = AF_INET;
mul_adr.sin_addr.s_addr = inet_addr(MULTICAST_ADDRESS);
mul_adr.sin_port = htons(PORT);
// set TTL option to Mulitcast
int time_live = TTL;
setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_TTL, (void*)&time_live, sizeof(time_live));
FILE* fp;
if((fp=fopen("news.txt", "r")) == NULL)
error_handling("fopen() error");
char buf[BUF_SIZE] = {};
// broad casting
while(!feof(fp))
{
fgets(buf, BUF_SIZE, fp);
sendto(send_sock, buf, strlen(buf), 0, (struct sockaddr*)&mul_adr, sizeof(mul_adr));
sleep(1);
}
fclose(fp);
close(send_sock);
return 0;
}
수신자 코드
int main(int argc, char* argv[])
{
int recv_sock = socket(PF_INET, SOCK_DGRAM, 0);
struct sockaddr_in adr;
memset(&adr, 0, sizeof(adr));
adr.sin_family = AF_INET;
adr.sin_addr.s_addr = htonl(INADDR_ANY);
adr.sin_port = htons(PORT);
if(bind(recv_sock, (struct sockaddr*)&adr, sizeof(adr)) == -1)
error_handling("bind() error");
struct ip_mreq join_adr;
join_adr.imr_multiaddr.s_addr = inet_addr(MULTICAST_ADDRESS);
join_adr.imr_interface.s_addr = htonl(INADDR_ANY);
// multicast group join
setsockopt(recv_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void*)&join_adr, sizeof(join_adr));
char buf[BUF_SIZE];
while(1)
{
int 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;
}
브로드 캐스트 방식의 데이터 전송
네트워크에 연결되어 있는 모든 호스트에게 동시에 데이터를 전송하기 위한 방법이다.
- Directed 브로드 캐스트: 네트워크 주소를 제외한 나머지 호스트 주소를 전부 1로 설정한다.
- Local 브로드 캐스트: 255.255.255.255 IP 주소로 전송한다.
- 전송자 측
소켓의 기본 옵션으로 브로드캐스트 옵션이 꺼져있으므로 옵션 설정을 통해 브로드 캐스트기반의 데이터 전송이 가능함을 설정한다.
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));
위를 설정하는 것을 포함하여 기본 UDP 전송 예제 코드에서 전송하는 IP 주소를 브로드 캐스트 형식의 IP주소로 설정하는 것으로 동일하다.
송신자 코드
int main(int argc, char* argv[])
{
int send_sock = socket(PF_INET, SOCK_DGRAM, 0);
struct sockaddr_in broad_adr = {};
broad_adr.sin_family = AF_INET;
broad_adr.sin_addr.s_addr = inet_addr(BROADCAST_ADDRESS);
broad_adr.sin_port = htons(PORT);
int so_brd = 1;
setsockaopt(send_sock, SOL_SOCKET, SO_BROADCAST, (void*)&so_brd, sizeof(so_brd));
FILE* fp;
if((fp=fopen("news.txt", "r"))==NULL)
error_handling("fopen() error");
char buf[BUF_SIZE];
while(!feof(fp))
{
fgets(buf, BUF_SIZE, fp);
sendto(send_sock, buf, strlen(buf), 0, (struct sockaddr*)&broad_adr, sizeof(broad_adr));
sleep(1);
}
close(send_sock);
return 0;
}
수신자 코드
int main(int argc, char* argv[])
{
int recv_sock = socket(PF_INET, SOCK_DGRAM, 0);
struct sockaddr_in adr = {};
adr.sin_family = AF_INET;
adr.sin_addr.s_addr = htonl(INADDR_ANY);
adr.sin_port = htons(atoi(PORT));
if(bind(recv_sock, (struct sockaddr*)&adr, sizeof(adr)) == -1)
error_handling("bind() error");
char buf[BUF_SIZE] = {};
while(1)
{
int 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;
}
'Computer Science 기본 지식 > 소켓 프로그래밍' 카테고리의 다른 글
[TCP/IP 소켓 프로그래밍] (11-2) 다중 접속 서버 - 레벨 트리거/엣지 트리거 (0) | 2021.04.05 |
---|---|
[TCP/IP 소켓 프로그래밍] (11-1) 다중 접속 서버 - epoll (0) | 2021.04.05 |
[TCP/IP 소켓 프로그래밍] (9) TCP 긴급 메세지 (0) | 2021.04.01 |
[TCP/IP 소켓 프로그래밍] (8) 다중 접속 서버 - 멀티 플렉싱 기반 select 서버 (0) | 2021.04.01 |
[TCP/IP 소켓 프로그래밍] (7-3) 다중 접속 서버 - 프로세스 간 통신 IPC (0) | 2021.03.31 |