멀티캐스트
멀티캐스트 서버가 멀티캐스트 그룹에게 데이터를 전송하면 그룹의 클라이언트는 모두 데이터를 수신합니다. 멀티캐스트 그룹의 수는 IP 주소 범위 내에서 무제한으로 추가할 수 있습니다. (데이터를 수신하려면 멀티캐스트의 그룹에 가입하면 됩니다.)
멀티캐스트는 UDP를 기반으로 하지만 UDP패킷과 다르게 하나의 패킷만 네트워크에 띄워놓으면 라우터들이 패킷을 복사해서 호스트들에게 전달합니다.
멀티캐스트 패킷의 전송을 위해선 TTL 설정이 필요한데, Time To Live의 약자로 정수로 표현되며 라우터를 하나 거칠 때마다 1씩 감소합니다. 만약 0이된다면 패킷은 더 이상 전달되지 않습니다.
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));
소켓은 UDP소켓으로 생성하고, IPPROTO_IP 레벨의 IP_MULTICAST_TTL 옵션을 설정해주면 됩니다. 위 예시에서의 TTL은 64입니다.
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));
...
멀티캐스트 그룹 가입도 UDP소켓을 생성하고, IPPROTO_IP레벨의 IP_ADD_MEMBERSHIP 옵션을 설정해주면 됩니다. 멀티 캐스트 그룹 주소와 호스트의 주소로 설정하는데, 여기서 사용되는 구조체 ip_mreq는 다음과 같습니다.
struct ip_mreq{
struct in_addr imr_multiaddr;
struct in_addr imr_interface;
}
in_addr 구조체는 전에 다뤘듯이 32비트 IPv4 인터넷 주소를 가집니다. imr_multiaddr에는 가입할 그룹의 IP주소, imr_interface에는 호스트의 IP 주소를 채우게 됩니다. (자신의 IP주소는 INADDR_ANY 사용해도 됩니다)
브로드캐스트
브로드캐스트 또한 호스트들에게 동시에 데이터를 전송하기 위한 방법입니다. Directed 브로드캐스트와 Local 브로드캐스트로 나뉘어있는데, 이는 IP주소에 따라 나뉩니다. Directed 브로드캐스트의 IP주소는 네트워크 주소를 제외한 나머지 호스트 주소의 비트를 1로 설정합니다. 192.12.34가 네트워크 주소이면, 192.12.34.255로 데이터를 전송하면 됩니다.
Local 브로드캐스트는 192.12.34 네트워크에 연결돼있는 호스트가 255.255.255.255에 데이터를 전송하면 192.12.34 네트워크의 모든 호스트에게 데이터가 전달됩니다.
기본 옵션의 소켓은 브로드캐스트 기반 데이터 전송이 불가능하므로, 따로 설정해줘야합니다.
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));
UDP소켓을 생성하고 SOL_SOCKET 레벨의 SO_BROADCAST 옵션을 1로 설정해주면 됩니다.
#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 <BroadCast 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));
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);
}
#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);
}
위 두 코드는 각각 브로드캐스트의 sender와 receiver의 예제입니다. 멀티캐스트와 브로드캐스트는 서버-클라이언트 대신 sender-receiver라는 표현을 사용합니다.
'현생 > TCP 소켓 프로그래밍' 카테고리의 다른 글
16. 파일 포인터 기반 입출력 스트림 분리에서 Half-close [TCP/IP][C][LINUX] (0) | 2022.02.16 |
---|---|
15. 표준 입출력 함수와 파일 디스크립터의 비교 및 변환 [TCP/IP][C][LINUX] (0) | 2022.02.11 |
13. 다양한 입출력 함수 [TCP/IP][C][LINUX] (0) | 2022.02.05 |
12. 멀티플렉싱과 select 함수 [TCP/IP][C][LINUX] (0) | 2022.02.04 |
11. 파이프 기반의 프로세스간 통신 [TCP/IP][C][LINUX] (0) | 2022.01.31 |