다음 이전 차례

6. 멀티캐스트 프로그래밍

멀티캐스트 프로그래밍... 혹은, 우리만의 응용프로그램 작성하기.

멀티캐스트를 지원하기 위해서 몇 가지 확장된 응용 프로그래밍 인터페이스(API)가 필요하다. 그 가운데 대부분은 setsockopt() (커널에 정보를 전달함) 와 getsockopt() (멀티캐스트 관련 정보 구함) 두 가지 시스템 호출을 통해 처리한다. 새로운 시스템 호출 두개가 멀티캐스트를 지원하기 위해 추가되었다는 뜻이 아니다. setsockopt()/getsockopt()는 4.2 BSD 시절부터 있었다. 단지 커널에 전달해 주어야하는 새로운 멀티캐스트 옵션이 추가되었을 뿐이다.

setsockopt()/getsockopt() 함수의 선언이다.


      int getsockopt(int s, int level, int optname, void* optval, int* optlen);

       int setsockopt(int s, int level, int optname, const void* optval, int optlen);

첫 번째 변수 s는 시스템 호출을 적용할 소켓이다. 멀티캐스트에서 이 소켓은 AF_INET 계열 이어야하며, 소켓 타입은 SOCK_DGRAM 또는 SOCK_RAW를 쓸 수 있다. 대부분은 SOCK_DGRAM으로 쓰지만, 라우팅 데몬을 제작하거나 수정할 계획이라면 SOCK_RAW 이 필요할지 모른다.

두 번째 변수 level은 옵션이나, 질의(query), 메시지(message)를 처리할 레이어를 의미한다. 소켓 레이어는 SOL_SOCKET, IP 레이어는 IPPROTO_IP, 등등이다. 멀티캐스트 프로그래밍에 level은 항상 IPPROTO_IP 이어야 한다.

optname은 우리가 설정 값을 지정하는 것인지 얻으려 하는 것인지를 의미한다. 설정 값 자체는(커널에 설정하거나 커널로부터 읽어오는 두 경우 모두) optval이다. 멀티캐스트 프로그래밍에서 optnames는 다음과 같다.

      optname              setsockopt()          getsockopt()
  IP_MULTICAST_LOOP           yes                     yes
  IP_MULTICAST_TTL            yes                     yes
  IP_MULTICAST_IF             yes                     yes
  IP_ADD_MEMBERSHIP           yes                      no
  IP_DROP_MEMBERSHIP          yes                      no

optlen은 optval이 가리키는 데이터구조의 크기를 의미한다. 단, getsockopt()에서는 반환 값이 된다. 커널은 optname 값을 optval이 가리키는 버퍼에 써넣은 후 optlen을 통해 자료의 크기를 알려준다.

setsockopt()/getsockopt()모두 실행 결과 성공하면 0, 에러가 발생하면 -1을 반환한다.

6.1 IP_MULTICAST_LOOP.

프로그래머라면, 데이터를 호스트로 루프백 시킬 것인지 결정해야한다. 만일, 프로세스가 여러 개이거나 데이터를 리스닝"listening"중인 사용자가 있다면 루프백을 가능하게 해야한다. 하지만, 카메라 이미지를 전송하는데 자기 화면에서 이미지를 볼 필요가 없다면, 아마도 루프백이 필요 없을 것이다. 후자의 경우 프로그램은 컴퓨터에 부착된 카메라로부터 영상자료를 이미 가지고 있을 것이기 때문에, 다시 소켓으로부터 자료를 받기 원하는 경우는 드물 것이다. 루프백은 기본 값으로 활성화되어 있다.

optval이 포인터이기 때문에 다음처럼 해서는 안 된다.

setsockopt(socket, IPPROTO_IP, IP_MULTICAST_LOOP, 0, 1);

루프백을 정지시키려면 다음과 같이 하고

u_char loop; setsockopt(socket, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));

루프백을 활성화시키려면 loop를 1로, 중지시키려면 0으로 설정한다.

소켓의 현재 루프백상태를 확인하려면 다음과 같이 한다.

u_char loop; int size;

getsockopt(socket, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, &size)

6.2 IP_MULTICAST_TTL.

특별히 지정하지 않는다면, 멀티캐스트 데이터그램은 지역 네트웍 외부로 전송되는 것을 방지하기 위해 TTL 1을 기본 값으로 전송된다. TTL을 원하는 값으로(0에서 255까지) 바꾸기 위해서 TTL값을 변수에 넣고 프로그램에서 다음과 같이 써주면 된다.(여기서는 "ttl"이라고 했다.)

u_char ttl; setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));

IP_MULTICAST_LOOP에서와 흡사하다.

6.3 IP_MULTICAST_IF.

일반적으로 시스템 관리자는 기본 인터페이스를 지정하며, 멀티캐스트 데이터그램은 그쪽으로 보내진다. 프로그래머는 이 옵션을 사용하여 지정된 기본 인터페이스를 무시하고 정해진 소켓에서 사용할 인터페이스를 선택할 수 있다.

struct in_addr interface_addr; setsockopt (socket, IPPROTO_IP, IP_MULTICAST_IF, &interface_addr, sizeof(interface_addr));

이후부터 이 소켓에서 생성된 모든 멀티캐스트 트래픽은 위에서 선택한 인터페이스로 출력된다. 원래 설정을 회복하여 관리자의 설정을 무시하고 커널이 외부 출력 인터페이스를 선택하도록 하려면 인터페이스에 INADDR_ANY를 설정하고 위와 동일한 옵션으로 setsockopt()를 호출한다.

외부 출력 인터페이스를 선택함에 있어서, 다음 ioctl이 유용할 것이다. SIOCGIFADDR (인터페이스 주소 얻기), SIOCGIFCONF (전체 인터페이스 목록 얻기), SIOCGIFFLAGS (인터페이스 상태 플래그(flag)를 얻어서 인터페이스가 멀티캐스트 가능한지 확인 - IFF_MULTICAST 플래그-).

호스트가 멀티캐스트 라우터로 작동한다면, 여러 인터페이스를 가지고 있거나 IP_MULTICAST_IF옵션이 설정되어 있지 않을 경우, 다른 인터페이스에서 멀티캐스트 포워딩이 가능하더라도 멀티캐스트는 기본 인터페이스를 통해 이루어진다.

6.4 IP_ADD_MEMBERSHIP.

커널에 우리가 어떤 그룹에 관심이 있는지 알려주어야 한다고 했던 것을 다시 생각해 보자. 그룹에 관심 있는 프로세스가 하나도 없다면, 그 그룹을 향해서 전송되어오는 패킷은 모두 폐기된다. 따라서 커널에 우리의 관심 그룹을 알리고, 그룹의 회원(member)이 되기 위해서는 다음과정이 필요하다. 우선 ip_mreq 구조체의 내용을 채운다. 그리고, optval을 설정하여 setsockopt()를 호출해서 커널에 이 구조체를 넘겨주면 된다.

ip_mreq 구조체는 (/usr/include/linux/in.h에 있음) 다음과 같다.


struct ip_mreq
{
        struct in_addr imr_multiaddr;   /* IP multicast address of group */
        struct in_addr imr_interface;   /* local IP address of interface */
};

주의 : 이 구조체의 "물리적" 정의는 위의 파일에 명시되어 있다. 하지만, 코드의 이식성을 위해서 <linux/in.h>을 포함(include)시켜서는 안 된다. 대신 <linux/in.h>를 포함하는 <netinet/in.h>을 포함시켜라.

첫 번째 멤버, imr_multiaddr는 우리가 참여할 그룹주소를 말한다. 회원(membership)은 그룹뿐만 아니라 인터페이스에도 관련이 있다는 점을 명심해야한다. 따라서, 두 번째 멤버, imr_interface값도 알려주어야 한다. 우리가 분산된 호스트에 있더라도 이러한 방식으로 각기 다른 인터페이스를 이용하여 같은 그룹에 참여할 수 있다. imr_interface는 와일드카드 주소(INADDR_ANY)를 이용할 수 있으며 이 경우 인터페이스를 선택하는 일은 커널이 한다.

일단 이 구조체를 채우면 (struct ip_mreq mreq;로 정의 했다고 하면) 다음 방법으로 setsockopt()를 호출하면 된다.

setsockopt (socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));

동일 소켓으로 여러 그룹에 참여 할 수 있다는 점에 주목하라. 그룹 수 제한은 IP_MAX_MEMBERSHIPS에 정의되어 있으며 커널버전 2.0.33에서 20이다.

6.5 IP_DROP_MEMBERSHIP.

이 과정은 그룹에 참여하는 것과 유사하다.

struct ip_mreq mreq; setsockopt (socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));

여기서 mreq는 그룹에 참여 할 때 사용했던 것과 동일한 구조체다. imr_interface에 INADDR_ANY를 사용한다면 커널이 선택한 첫 번째 인터페이스가 제거된다.

같은 소켓으로 아주 많은 그룹에 참여했을 경우 연결을 종료하기 위해 모든 회원 관계를 삭제할 필요는 없다. 소켓을 닫으면 연결된 모든 회원 관계는 커널이 의해 삭제된다. 소켓을 연 프로세스를 종료(kill)시킬 때도 동일하다.

끝으로 그룹에서 회원을 제거하는 과정이 호스트로 하여금 그 그룹으로 오는데이터그램을 받지 못하도록 하는 것을 의미하는 것은 아니다. 동일 인터페이스로 그 그룹에 참여한 소켓이 IP_DROP_MEMBERSHIP의 상태가 되더라도 호스트는 그룹 멤버의 목록을 보관한다.

ADD_MEMBERSHIP 과 DROP_MEMBERSHIP 은 성공, 실패 여부를 즉시 반환한다. (nonblocking operations)


다음 이전 차례