マルチキャスト

 

1. 定義

マルチキャスト: マルチキャストグループに属するユーザに対してのみパケットを配送する仕組み。外部のマルチキャストグループからのパケットを受信するためには、少なくとも所属ドメインのルータがマルチキャストルーティングプロトコルを起動・設定していることが必要。

 

2. マルチキャストの使用例

マルチキャスト対応のインターネット会議、インターネット放送。

 

3. マルチキャスト用ソケット関数

通常のソケット関数に同じ。ただし、マルチキャスト用に、ip_mreq 構造体とソケットオプションの設定が必要になる。これだけで済むのは、ソケット側で対応してくれている恩恵である。

 

4. 簡単なマルチキャストプログラム

以下は、マルチキャストパケットの送信元が1秒ごとにメッセージをマルチキャストするプログラムである (ただし、エラー処理をまったく行っていない悪いプログラムである)。まず送信側を起動し、別のマシンで受信側を起動する。先週のソケットと同じく、Windows 用と Linux 用のプログラムの違いはほとんどない。

Windows版: リンクオプションに ws2_32.lib を加える。

マルチキャスト送信側

#include <stdio.h>
#include <winsock2.h>
#include <ws2tcpip.h>

int
main() {
  /* IPアドレス、ポート番号、ソケット */
  char destination[80];
  unsigned short port = 9876;
  int destSocket;

  /* sockaddr_in 構造体 */
  struct sockaddr_in destSockAddr;

  /* 各種パラメータ */
  int status;
  int enable = 1;
  int numsnt;
  unsigned long on = 1;
  char *toSendText = "This is a test";
  int count = 0;
  
  /* マルチキャスト用追加 */
  struct sockaddr_in addrLocal;
  struct ip_mreq stMreq;
  u_long lTTL = 2;

  /************************************************************/
  /* Windows 独自の設定 */
  WSADATA data;
  WSAStartup(MAKEWORD(2,0), &data));

  /* マルチキャストアドレスの入力 */
  printf("Multicast address ? ");
  scanf("%s", destination);

  /* sockaddr_in 構造体のセット */
  memset(&destSockAddr, 0, sizeof(destSockAddr));
  destSockAddr.sin_addr.s_addr = inet_addr(destination);
  destSockAddr.sin_port = htons(port);
  destSockAddr.sin_family = AF_INET;

  /* ソケット生成 */
  destSocket = socket(AF_INET, SOCK_DGRAM, 0);
 
  /* マルチキャスト用追加 */
  addrLocal.sin_family = AF_INET;
  addrLocal.sin_addr.s_addr = htonl(INADDR_ANY);
  addrLocal.sin_port = 0;
  bind(destSocket, (struct sockaddr_in *) &addrLocal, sizeof(addrLocal));
  stMreq.imr_multiaddr.s_addr = destSockAddr.sin_addr.s_addr;
  stMreq.imr_interface.s_addr = INADDR_ANY;
  setsockopt(destSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*) &stMreq, sizeof(stMreq)); // メンバシップ
  setsockopt(destSocket, IPPROTO_IP, IP_MULTICAST_TTL, (char*) &lTTL, sizeof(lTTL)); // TTL

  /* パケット送出 */
  while (1) {
    printf("sending... [%d]\n", count++);
    sendto(destSocket, toSendText, strlen(toSendText)+1, 0, &destSockAddr, sizeof(destSockAddr));
    Sleep(1000);
  }

  /* Windows 独自の処理 */
  WSACleanup();
}

マルチキャスト受信側

#include <stdio.h>
#include <winsock2.h>
#include <ws2tcpip.h>

#define BUFFER_SIZE 256

int
main() {
  /* ポート番号、ソケット */
  unsigned short port = 9876;
  int recvSocket;

  /* sockaddr_in 構造体 */
  struct sockaddr_in recvSockAddr;

  /* 各種パラメータ */
  int status;
  int numrcv;
  char buffer[BUFFER_SIZE];
  int length;
  unsigned long on = 1;

  /* マルチキャスト用追加 */
  char address[80]; // マルチキャストアドレス
  struct ip_mreq stMreq;

  /************************************************************/
  /* Windows 独自の設定 */
  WSADATA data;
  WSAStartup(MAKEWORD(2,0), &data));

  /* マルチキャストアドレスの入力 */
  printf("Multicast address ? ");
  scanf("%s", address);

  /* sockaddr_in 構造体のセット */
  memset(&recvSockAddr, 0, sizeof(recvSockAddr));
  recvSockAddr.sin_port = htons(port);
  recvSockAddr.sin_family = AF_INET;
  recvSockAddr.sin_addr.s_addr = htonl(INADDR_ANY);

  /* ソケット生成 */
  recvSocket = socket(AF_INET, SOCK_DGRAM, 0);

  /* バインド */
  bind(recvSocket, (struct sockaddr_in *) &recvSockAddr, sizeof(recvSockAddr));

  /* マルチキャスト用追加 */
  stMreq.imr_multiaddr.s_addr = inet_addr(address);
  stMreq.imr_interface.s_addr = INADDR_ANY;
  setsockopt(recvSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*) &stMreq, sizeof(stMreq)); // メンバシップ

  /* パケット受信 */
  while(1) {
    numrcv = recvfrom((int) recvSocket, (char *) buffer, BUFFER_SIZE, 0, (struct sockaddr *) NULL, (int *) &length);
    if(numrcv == -1) break;
    printf("received: %s\n", buffer);
  }
  
  /* Windows 独自の処理 */
  closesocket(recvSocket);
  WSACleanup();
}

 

Linux版: コンパイルは "gcc ソース名 -lsocket" とする。

マルチキャスト送信側

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

int
main() {
  /* IPアドレス、ポート番号、ソケット */
  char destination[80];
  unsigned short port = 9876;
  int destSocket;

  /* sockaddr_in 構造体 */
  struct sockaddr_in destSockAddr;

  /* 各種パラメータ */
  int status;
  int enable = 1;
  int numsnt;
  unsigned long on = 1;
  char *toSendText = "This is a test";
  int count = 0;
  
  /* マルチキャスト用追加 */
  struct sockaddr_in addrLocal;
  struct ip_mreq stMreq;
  u_long lTTL = 2;

  /************************************************************/
  /* マルチキャストアドレスの入力 */
  printf("Multicast address ? ");
  scanf("%s", destination);

  /* sockaddr_in 構造体のセット */
  memset(&destSockAddr, 0, sizeof(destSockAddr));
  destSockAddr.sin_addr.s_addr = inet_addr(destination);
  destSockAddr.sin_port = htons(port);
  destSockAddr.sin_family = AF_INET;

  /* ソケット生成 */
  destSocket = socket(AF_INET, SOCK_DGRAM, 0);
 
  /* マルチキャスト用追加 */
  addrLocal.sin_family = AF_INET;
  addrLocal.sin_addr.s_addr = htonl(INADDR_ANY);
  addrLocal.sin_port = 0;
  bind(destSocket, (struct sockaddr_in *) &addrLocal, sizeof(addrLocal));
  stMreq.imr_multiaddr.s_addr = destSockAddr.sin_addr.s_addr;
  stMreq.imr_interface.s_addr = INADDR_ANY;
  setsockopt(destSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*) &stMreq, sizeof(stMreq)); // メンバシップ
  setsockopt(destSocket, IPPROTO_IP, IP_MULTICAST_TTL, (char*) &lTTL, sizeof(lTTL)); // TTL

  /* パケット送出 */
  while (1) {
    printf("sending... [%d]\n", count++);
    sendto(destSocket, toSendText, strlen(toSendText)+1, 0, &destSockAddr, sizeof(destSockAddr));
    sleep(1);
  }
}

マルチキャスト受信側

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#define BUFFER_SIZE 256

int
main() {
  /* ポート番号、ソケット */
  unsigned short port = 9876;
  int recvSocket;

  /* sockaddr_in 構造体 */
  struct sockaddr_in recvSockAddr;

  /* 各種パラメータ */
  int status;
  int numrcv;
  char buffer[BUFFER_SIZE];
  int length;
  unsigned long on = 1;

  /* マルチキャスト用追加 */
  char address[80]; // マルチキャストアドレス
  struct ip_mreq stMreq;

  /************************************************************/
  /* マルチキャストアドレスの入力 */
  printf("Multicast address ? ");
  scanf("%s", address);

  /* sockaddr_in 構造体のセット */
  memset(&recvSockAddr, 0, sizeof(recvSockAddr));
  recvSockAddr.sin_port = htons(port);
  recvSockAddr.sin_family = AF_INET;
  recvSockAddr.sin_addr.s_addr = htonl(INADDR_ANY);

  /* ソケット生成 */
  recvSocket = socket(AF_INET, SOCK_DGRAM, 0);

  /* バインド */
  bind(recvSocket, (struct sockaddr_in *) &recvSockAddr, sizeof(recvSockAddr));

  /* マルチキャスト用追加 */
  stMreq.imr_multiaddr.s_addr = inet_addr(address);
  stMreq.imr_interface.s_addr = INADDR_ANY;
  setsockopt(recvSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*) &stMreq, sizeof(stMreq)); // メンバシップ

  /* パケット受信 */
  while(1) {
    numrcv = recvfrom((int) recvSocket, (char *) buffer, BUFFER_SIZE, 0, (struct sockaddr *) NULL, (int *) &length);
    if(numrcv == -1) break;
    printf("received: %s\n", buffer);
  }
  
  /* ソケット終了 */
  close(recvSocket);
}