マルチキャスト
マルチキャスト: マルチキャストグループに属するユーザに対してのみパケットを配送する仕組み。外部のマルチキャストグループからのパケットを受信するためには、少なくとも所属ドメインのルータがマルチキャストルーティングプロトコルを起動・設定していることが必要。
マルチキャスト対応のインターネット会議、インターネット放送。
通常のソケット関数に同じ。ただし、マルチキャスト用に、ip_mreq 構造体とソケットオプションの設定が必要になる。これだけで済むのは、ソケット側で対応してくれている恩恵である。
以下は、マルチキャストパケットの送信元が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); } |