멀티케스트 소켓 구현 문제 해결해주세요 ㅠ

lwsang21의 이미지

리눅스 환경에서 멀티케스트 소켓통신을 C로 구현했는데 로컬네트워크에서는 잘 작동하는데 인터네트워크에서는 수신단 쪽에 패킷이 안들어옵니다.
정말 오랫동안 해결하려고 별 짓을 다했지만 모르겠네요,,,
해결해주시면 정말 감사하겠습니다.
급하게 작성한다고 주석은 엉망입니다,,,

<송신단>

//SERVER SOCKET
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
 
#define MULPORTNUM 9005
#define TPPORTNUM 9000
#define BROADNUM 10
#define NODENUM 2
 
int main(void) {
    char buf[256], ipad[NODENUM][INET_ADDRSTRLEN];
    struct sockaddr_in mult, sin, cli;
    struct in_addr localaddr;
    float beacon_interval = 0;
    int min_offset = 0, offset = 0, optval = 1;
    int mt, sd, nsd[NODENUM], min, iter = 0, clen = sizeof(cli), multiTTL = 64;
    int32_t offset_time = 0, offset_utime = 0, loc_time[NODENUM][BROADNUM], loc_utime[NODENUM][BROADNUM], My_time[NODENUM][BROADNUM], My_utime[NODENUM][BROADNUM];
 
    if ((mt = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1){ //UDP socket을 생성(연결 성공시 0을 리턴 실패시 -1을 리턴)
        perror("socket"); //에러메시지를 출력하는 함수
        exit(1); //1을 반환하면서 프로그램 종료
    }
    if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1){ //TCP socket을 생성(연결 성공시 0을 리턴 실패시 -1을 리턴)
        perror("socket"); //에러메시지를 출력하는 함수
        exit(1); //1을 반환하면서 프로그램 종료
    }
 
    if ((setsockopt(mt , IPPROTO_IP, IP_MULTICAST_TTL, (char*)&multiTTL, sizeof(multiTTL))) == -1){ 
        perror("setsockopt"); //에러메시지를 출력하는 함수
        exit(1); //1을 반환하면서 프로그램 종료
    }
    if ((setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))) == -1){ 
        perror("setsockopt"); //에러메시지를 출력하는 함수
        exit(1); //1을 반환하면서 프로그램 종료
    }
 
    memset((char *)&mult, '\0', sizeof(mult)); //socket 구조체에 값을 지정(&ser-메모리의 시작 주소, \0-메모리에 채우고자 하는 값, size-채우고자하는 메모리의 크기)
    mult.sin_family = AF_INET; //socket family를 AF_INET으로 지정
    mult.sin_port = htons(MULPORTNUM); 
    mult.sin_addr.s_addr = inet_addr("224.0.1.222"); //소켓 주소 구조체에 서버의 주소를 지정
 
    localaddr.s_addr = htonl(INADDR_ANY);;
    setsockopt(mt , IPPROTO_IP, IP_MULTICAST_IF, (char*)&localaddr, sizeof(localaddr));
 
    memset((char *)&sin, '\0', sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(TPPORTNUM); 
    sin.sin_addr.s_addr = htonl(INADDR_ANY);; //소켓 주소 구조체에 서버의 주소를 지정
 
    if (bind(sd, (struct sockaddr *)&sin, sizeof(sin))) { 
        perror("bind");
        exit(1);
    }
 
    // Broadcasting phase
    for (int n = 1; n <= BROADNUM; n++) {
        sprintf(buf, "%d", n);
 
        if ((sendto(mt, buf, strlen(buf)+1, 0, (struct sockaddr *)&mult, sizeof(mult))) == -1) { //클라이언트에게 sendto 함수로 msg를 전송(mt, buf-전송할 msg를 저장한 메모리 주소, msg의 크기, 0-데이터를 주고받는 방법을 지정한 플래그, &cli-msg를 전송할 호스트의 주소, &clientlen-&cli의 크기)
            perror("sendto");
            exit(1);         
        }
 
        sleep(beacon_interval);
    }
    strcpy(buf, "END"); //종료문전달 
 
    if ((sendto(mt, buf, strlen(buf)+1, 0, (struct sockaddr *)&mult, sizeof(mult))) == -1) { //클라이언트에게 sendto 함수로 msg를 전송(mt, buf-전송할 msg를 저장한 메모리 주소, msg의 크기, 0-데이터를 주고받는 방법을 지정한 플래그, &cli-msg를 전송할 호스트의 주소, &clientlen-&cli의 크기)
        perror("sendto");
        exit(1);         
    }
 
 
    // Time stamp phase
    if (listen(sd, NODENUM)) { //cli로 부터 time값을 수신받기위해 대기
        perror("listen");
        exit(1);
    }
 
    for (int n = 0; n < NODENUM; n++) {
        if ((nsd[n] = accept(sd, (struct sockaddr *)&cli, &clen)) == -1) { //accept 함수로 클라이언트의 접속 요청을 수락하고 새로운 소켓 기술자를 생성해 nsd에 저장(sd, &cli-client의 IP, &clen-cli의 크기)
            perror("accept");
            exit(1);
        }
 
        inet_ntop(AF_INET, &(cli.sin_addr), ipad[n], INET_ADDRSTRLEN);
 
        if (recv(nsd[n], &loc_time[n][0], sizeof(loc_time[n])+1, 0) == -1) {
            perror("recv");
            exit(1);
        }
 
        if (recv(nsd[n], &loc_utime[n][0], sizeof(loc_utime[n])+1, 0) == -1) {
            perror("recv");
            exit(1);
        }
    }
 
    /*for (int n = 0; n < NODENUM; n++) {
        for (int i = 0; i < BROADNUM; i++) {
            printf("%i-th client %i-th stamp: %d.%d\n", n+1, i+1, loc_time[n][i], loc_utime[n][i]);
        }
    }*/
 
    // Offset phase
    for (int n = 0; n < NODENUM; n++) {
        for (int i = 0; i < BROADNUM; i++) { // 기준 node 설정을 위한 기지국과 단말간 offset 계산
 
            if (loc_time[n][i] != 0){ // 손실된 UDP는 무시
                offset_time = offset_time + My_time[n][i] - loc_time[n][i];
                offset_utime = offset_utime + My_utime[n][i] - loc_utime[n][i];
            }
        }
        offset = offset_time*1000000 + offset_utime;
 
        if (n == 0) {
            min = n;
            min_offset = offset;
        }
        else if (offset < min_offset) {
            min = n;
            min_offset = offset;
        }
 
        //printf("%i-th client offset: %d\n", n+1, offset);
        offset = 0; 
        offset_time = 0;
        offset_utime = 0;
    }
    //printf("%i-th client has minimum offset: %d\n", min+1, min_offset);
 
    for (int n = 0; n < NODENUM; n++) {
        if (n != min) {
            for (int i = 0; i < BROADNUM; i++) { // 기준 node와 다른 node 간 offset 측정
 
                if (loc_time[n][i] != 0 && loc_time[min][i] != 0){ // 손실된 UDP는 무시
                    offset_time = offset_time + loc_time[min][i] - loc_time[n][i];
                    offset_utime = offset_utime + loc_utime[min][i] - loc_utime[n][i];
                    iter++;
                }
 
            }
            offset_time = offset_time / iter;
            offset_utime = offset_utime / iter;
 
            strcpy(buf, "Adjust Offset");
            send(nsd[n], buf, strlen(buf)+1, 0);
            send(nsd[n], &offset_time, sizeof(offset_time)+1, 0);
            send(nsd[n], &offset_utime, sizeof(offset_utime)+1, 0);
            printf("RBS synchronization %ius\n", abs(offset_time*1000000)+abs(offset_utime));
 
            iter = 0; 
            offset_time = 0;
            offset_utime = 0;
        }
 
        else if (n == min){
            strcpy(buf, "END"); //종료문전달
            send(nsd[n], buf, strlen(buf)+1, 0);
            close(nsd[n]);
        }
    }
    close(sd);
    return 0;
}
<\code>
 
<수신단>
<code lang="c">
//CLIENT SOCKET
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdint.h>
 
#define MULPORTNUM 9005
#define TPPORTNUM 9000
#define BROADNUM 10
 
int main(void) {
    char buf[256], endst[50] = "END";
    struct sockaddr_in mult, sin;
    struct ip_mreq joinAddr;
    struct timeval tv_cli, current;
    int32_t offset_time, offset_utime, loc_time[BROADNUM], loc_utime[BROADNUM], timelen = 0; 
    int mt, sd, n, index, multlen = sizeof(mult), broadcast = 1;
 
    if ((mt = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1){ //socket을 생성(연결 성공시 0을 리턴 실패시 -1을 리턴)
        perror("socket"); //에러메시지를 출력하는 함수
        exit(1); //1을 반환하면서 프로그램 종료
    }
 
    memset((char *)&mult, '\0', sizeof(mult)); //socket 구조체에 값을 지정(&ser-메모리의 시작 주소, \0-메모리에 채우고자 하는 값, size-채우고자하는 메모리의 크기)
    mult.sin_family = AF_INET; //socket family를 AF_INET으로 지정
    mult.sin_port = htons(MULPORTNUM); 
    mult.sin_addr.s_addr = htonl(INADDR_ANY); //소켓 주소 구조체에 서버의 주소를 지정
 
    if (bind(mt, (struct sockaddr *)&mult, sizeof(mult))) { //17행에서 생성한 소켓을 bind 함수로 22~25행에서 설정한 IP, port 번호와 연결(실패시 -1을 리턴 성공시 0을 리턴)(mt-socket에서 선언한 구조체, &ser-AF_INET의 경우 sockaddr_in AF_UNIX의 경우 sockaddr, len-선언한 구조체의 크기)
        perror("bind");
        exit(1);
    }
 
    //멀티캐스트 그룹 가입을 위한 구조체
    joinAddr.imr_multiaddr.s_addr = inet_addr("224.0.1.222");    //Multicast group (=IP addr)
    joinAddr.imr_interface.s_addr = htonl(INADDR_ANY); 
    //joinAddr.imr_interface.s_addr = inet_addr("112.212.149.232:2000"); 
 
    if ((setsockopt(mt, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&joinAddr, sizeof(joinAddr))) == -1){ 
        perror("setsockopt"); 
        exit(1); 
    }
 
    while (1) {
 
    // Broadcasting phase
        timelen = 0;
        memset((int32_t *)&loc_time, '\0', sizeof(loc_time));
        memset((int32_t *)&loc_utime, '\0', sizeof(loc_utime));
        while (1) {
 
            n = recvfrom(mt, buf, 255, 0, (struct sockaddr *)&mult, &multlen); //서버가 보낸 msg를 recvfrom 함수로 수신(mt, buf-전송받은 msg를 저장할 메모리 주소, msg의 크기, 0-데이터를 주고받는 방법을 지정한 플래그, msg를 보내는 서버의 주소, 주소의 크기)
 
            index = atoi(buf);
 
            if (strcmp(endst, buf)==0 || timelen == BROADNUM)
                break;  
 
            if ((ioctl(mt, SIOCGSTAMP, &tv_cli)) == -1){ 
                perror("ioctl"); 
                exit(1); 
            }
 
            if (index == timelen+1){ // UDP 정상 수신시
                loc_time[index-1] = tv_cli.tv_sec;
                loc_utime[index-1] = tv_cli.tv_usec;
            }
            else{ // UDP 손실시
                loc_time[timelen] = 0;
                loc_utime[timelen] = 0;
                loc_time[index-1] = tv_cli.tv_sec;
                loc_utime[index-1] = tv_cli.tv_usec;
                timelen = index-1;
            }
 
            printf("** From Server : %i packet\n", index);
            printf("** Local clock : %i.%i\n", loc_time[timelen], loc_utime[timelen]);
 
            timelen++;
        }
        printf("END BRD\n");
 
    // Time stamp phase
        if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1){ 
            perror("socket");
            exit(1);
        }
 
        memset((char *)&sin, '\0', sizeof(sin)); //socket 구조체에 값을 지정(&ser-메모리의 시작 주소, \0-메모리에 채우고자 하는 값, size-채우고자하는 메모리의 크기)
        sin.sin_family = AF_INET; //socket family를 AF_INET으로 지정
        sin.sin_port = htons(TPPORTNUM); 
        sin.sin_addr.s_addr = inet_addr("210.107.214.194"); //소켓 주소 구조체에 서버의 주소를 지정
 
        if (connect(sd, (struct sockaddr *)&sin, sizeof(sin))) {
            perror("connect");
            exit(1);
        }
 
        if ((send(sd, &loc_time[0], sizeof(loc_time)+1, 0)) == -1) {
            perror("send");
            exit(1);
        }
        printf("** send time\n");
 
        if ((send(sd, &loc_utime[0], sizeof(loc_utime)+1, 0)) == -1) {
            perror("send");
            exit(1);
        }
        printf("** send utime\n");
 
 
    // Offset phase
        if (recv(sd, buf, sizeof(buf) + 1, 0) == -1) {
            perror("recv");
            exit(1);
        }
 
        if (strcmp(endst, buf) != 0){
            printf("** %s\n", buf);
            recv(sd, &offset_time, sizeof(offset_time)+1, 0);
            recv(sd, &offset_utime, sizeof(offset_utime)+1, 0);
 
            printf("** offset : %i.%i\n", offset_time, offset_utime);
 
            gettimeofday(&current, 0);
            printf("** current time : %i.%i\n", current.tv_sec, current.tv_usec);
 
            current.tv_sec = current.tv_sec + offset_time;
            current.tv_usec = current.tv_usec + offset_utime;
            printf("** change time : %i.%i\n", current.tv_sec, current.tv_usec);
 
            settimeofday(&current, 0); // 관리자 권한 필요
 
            gettimeofday(&current, 0);
            printf("** current time : %i.%i\n", current.tv_sec, current.tv_usec);
        }
        close(sd);
    }
    return 0;
}
<\code>
chanik의 이미지

잘은 모릅니다만..

1. 송신단과 수신단 사이에서 패킷을 중계하는 라우터나 브리지같은 장비가 있다면 해당 장비가 멀티캐스트 패킷을 전달하도록 설정돼 있어야 하고

2. 송신단/수신단 컴퓨터 각각에서, 사용하는 NIC가 여러개일 경우 어느 NIC를 통해 멀티캐스트 패킷이 드나드는지 확실히 하기 위한 라우트 엔트리가 추가돼야겠습니다. 예를 들면 이런 식으로요. $ sudo ip route add 224.0.1.222/32 dev eth0

lwsang21의 이미지

1. 일단 두개의 컴터 모두 각각 집과 직장의 공유기에 물려있어서 사이에 라우터나 브리지가 어떠한 방식으로 설정돼있는지 알 수가 없습니다ㅠ

2. 송수신단 모두 라즈베리파이 컴퓨터여서 NIC는 하나입니다 ㅠ

chanik의 이미지

인터넷으로 임의의 멀티캐스트 패킷을 직접 전송하는 것은 불가능하므로 어떤 형태로든 터널을 만들어야 할 겁니다. 일례로, VPN으로 집과 직장의 라즈베리파이 사이를 사설망으로 묶고 VPN을 통해 멀티캐스트 패킷을 전송한다든지 하는 식으로요. 저는 관련 경험이나 아는 바가 없네요..

pynoos의 이미지

멀티캐스팅이 인터넷 구간에서 실현되려면, 서버가 멀티캐스팅 그룹에 가입할 때,

setsockopt(mt, IPPROTO_IP, IP_ADD_MEMBERSHIP,...

* Layer 2를 담당하는 드라이버에 multicast 주소에 대한 수신을 허용하라는 명령이 내려가고,
* IGMP를 이용하여 Router에 해당 주소에 대한 routing 요청을 전달하게 됩니다.

그런데, IGMP에 대한 요청이 ISP들에 의해 무시되므로 인터넷을 이용한 Multicast가 쉽지 않습니다.

lwsang21의 이미지

그렇다면 혹시 위에 분 말씀대로 VPN을 이용한다면 가능할까요?
아니면 다른 방법이 있을까요,,,

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.