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