UDPパケットをネットワーク内の全デバイスへ送信する場合に、IPv4ではブロードキャストアドレス(ホスト部のビットが全て1)を使用します。
一方、IPv6にはブロードキャストアドレスが存在しないため、同様の処理を行いたい場合にはマルチキャストアドレス ff02::1 を使用し、全てのリンクローカルノードに対しマルチキャストを実行します。
IPv6マルチキャストはIPv4ブロードキャストとは異なり、ネットワークインターフェースの指定が必要です。
IPv4ブロードキャストと同じく、全てのネットワークインターフェースでIPv6マルチキャストを実行したい場合、ネットワークインターフェースを列挙しなければなりません。
ネットワークインターフェース列挙は規格化されていないため、OS / フレームワークにより実装方法が異なります。
以下のサンプルコードでは、ほぼ同一の処理をC++(Windows版とLinux版) / C# / Javaで実装しています。
サンプルコードMulticastSampleは、サーバーモードまたはクライアントモードで動作します。
同一ネットワーク内に、サーバーモードMulticastSampleを実行しているマシンがあった場合、クライアントモードMulticastSampleでそのIPアドレスを列挙することができます。
また、ほぼ同一の挙動をIPv4とIPv6の両方で試すことができます。(IPv4モードとIPv6モードは、互いに通信することはできません)
-s | サーバーモード(デフォルト) |
-c | クライアントモード |
-6 | IPv6モード(デフォルト) |
-4 | IPv4モード |
-p <num> | TCP/IPポート番号(デフォルト : 50000) |
// multicast sample program for C++ / Windows
#include <vector>
#include <set>
#include <string>
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <cstring>
#include <cstdint>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
// utility
#define ASSERT(cnd, msg) \
do { \
if (!(cnd)) { \
std::cout << (msg) << std::endl; \
std::exit(1); \
} \
} \
while (0)
class sockaddr_holder {
public:
union {
sockaddr* addr;
sockaddr_in* addr_in;
sockaddr_in6* addr_in6;
};
int len;
sockaddr_holder() :
addr(nullptr),
len(0)
{
}
sockaddr_holder(
const sockaddr_holder& other) :
addr(nullptr),
len(0)
{
assign(other.addr, other.len);
}
sockaddr_holder(
sockaddr_holder&& other) :
addr(nullptr),
len(0)
{
addr = other.addr;
len = other.len;
other.addr = nullptr;
other.len = 0;
}
sockaddr_holder(
sockaddr* new_addr,
int new_len) :
addr(nullptr),
len(0)
{
assign(new_addr, new_len);
}
~sockaddr_holder() {
assign(nullptr, 0);
}
static sockaddr_holder from(
ADDRESS_FAMILY family,
const void* addr_bytes = nullptr,
int port = 0,
int scope_id = 0)
{
sockaddr_holder addr(
nullptr,
(family == AF_INET)?
sizeof(sockaddr_in):
(family == AF_INET6)?
sizeof(sockaddr_in6):
// else
size_t(0));
addr.addr->sa_family = family;
if (family == AF_INET) {
if (addr_bytes != nullptr) {
memcpy(&addr.addr_in->sin_addr, addr_bytes, 4);
}
addr.addr_in->sin_port = htons(static_cast<std::uint16_t>(port));
} else if (family == AF_INET6) {
if (addr_bytes != nullptr) {
memcpy(&addr.addr_in6->sin6_addr, addr_bytes, 16);
}
addr.addr_in6->sin6_port = htons(static_cast<std::uint16_t>(port));
addr.addr_in6->sin6_scope_id = scope_id;
}
return addr;
}
static sockaddr_holder from_str(
ADDRESS_FAMILY family,
const char* addr_str,
int port = 0,
int scope_id = 0)
{
std::uint8_t addr_bytes[16];
int result = inet_pton(
family,
addr_str,
&addr_bytes);
ASSERT(result == 1, "error: inet_pton");
return from(family, addr_bytes, port, scope_id);
}
sockaddr_holder& operator=(
const sockaddr_holder& other)
{
if (&other != this) {
assign(other.addr, other.len);
}
return *this;
}
sockaddr_holder& operator=(
sockaddr_holder&& other)
{
if (&other != this) {
addr = other.addr;
len = other.len;
other.addr = nullptr;
other.len = 0;
}
return *this;
}
std::string to_string() const {
std::stringstream stream;
std::vector<char> nbuf(256);
if (addr == nullptr) {
stream << "null";
} else if (addr->sa_family == AF_INET) {
stream <<
inet_ntop(
addr_in->sin_family, &addr_in->sin_addr,
&nbuf[0], nbuf.size()) <<
':' <<
ntohs(addr_in->sin_port);
} else if (addr->sa_family == AF_INET6) {
stream <<
'[' <<
inet_ntop(
addr_in->sin_family, &addr_in6->sin6_addr,
&nbuf[0], nbuf.size()) <<
'%' <<
addr_in6->sin6_scope_id <<
"]:" <<
ntohs(addr_in6->sin6_port);
}
return stream.str();
}
private:
void assign(
sockaddr* new_addr,
int new_len)
{
if (addr != nullptr) {
std::free(addr);
addr = nullptr;
len = 0;
}
if (new_len > 0) {
addr = reinterpret_cast<sockaddr*>(std::malloc(new_len));
ASSERT(addr != nullptr, "error: malloc");
len = new_len;
if (new_addr != nullptr) {
std::memcpy(addr, new_addr, new_len);
} else {
std::memset(addr, 0, new_len);
}
}
}
};
// constants
const int default_port = 50000;
sockaddr_holder multicast_address =
sockaddr_holder::from_str(AF_INET6, "ff02::1");
const std::string request_message =
"Are you a MulticastSample server?";
const std::string response_message =
"Yes, I am a MulticastSample server!";
// common
std::vector<sockaddr_holder> listup_addresses(
bool is_ipv6,
int port,
bool strict)
{
std::vector<sockaddr_holder> addresses;
IP_ADAPTER_ADDRESSES* adapters = nullptr;
ULONG count = 15000*sizeof(std::uint8_t);
std::vector<std::uint8_t> buffer;
UINT result = 0;
for (int i = 0; i < 3; i++) {
buffer.resize(static_cast<size_t>(count));
adapters = reinterpret_cast<IP_ADAPTER_ADDRESSES*>(&buffer[0]);
result = GetAdaptersAddresses(
is_ipv6? AF_INET6: AF_INET, 0, nullptr,
adapters, &count);
if (result != ERROR_BUFFER_OVERFLOW) {
break;
}
}
ASSERT(result == NO_ERROR, "error: GetAdaptersAddresses");
std::set<std::uint32_t> listed;
if (!is_ipv6) {
for (auto a = adapters; a != nullptr; a = a->Next) {
if (((a->Flags & IP_ADAPTER_IPV4_ENABLED) == 0) ||
(a->IfType == IF_TYPE_PPP) ||
(a->IfType == IF_TYPE_SOFTWARE_LOOPBACK))
{
continue;
}
if (strict &&
(a->OperStatus != IfOperStatusUp))
{
continue;
}
for (auto u = a->FirstUnicastAddress; u != nullptr; u = u->Next) {
if (u->Address.lpSockaddr->sa_family != AF_INET) {
continue;
}
sockaddr_holder uaddr(
u->Address.lpSockaddr,
u->Address.iSockaddrLength);
auto addr_val = uaddr.addr_in->sin_addr.S_un.S_addr |
htonl(0xFFFFFFFFUL >> u->OnLinkPrefixLength);
if (listed.find(addr_val) != listed.end()) {
continue;
}
auto addr = sockaddr_holder::from(AF_INET, &addr_val, port);
addresses.push_back(addr);
listed.insert(addr_val);
}
}
} else {
for (auto a = adapters; a != nullptr; a = a->Next) {
if (((a->Flags & IP_ADAPTER_IPV6_ENABLED) == 0) ||
((a->Flags & IP_ADAPTER_NO_MULTICAST) != 0) ||
(a->IfType == IF_TYPE_PPP) ||
(a->IfType == IF_TYPE_SOFTWARE_LOOPBACK))
{
continue;
}
if (strict &&
(a->OperStatus != IfOperStatusUp))
{
continue;
}
bool has_ipv6_address = false;
for (auto u = a->FirstUnicastAddress; u != nullptr; u = u->Next) {
if (u->Address.lpSockaddr->sa_family == AF_INET6) {
has_ipv6_address = true;
break;
}
}
if (!has_ipv6_address) {
continue;
}
int scope_id = a->Ipv6IfIndex;
if (listed.find(scope_id) != listed.end()) {
continue;
}
auto addr = sockaddr_holder::from(
AF_INET6,
&multicast_address.addr_in6->sin6_addr, port, scope_id);
addresses.push_back(addr);
listed.insert(scope_id);
}
}
return addresses;
}
// server
void sample_server(
bool is_ipv6,
int port)
{
std::cout <<
"multicast-sample server for " <<
(!is_ipv6? "IPv4": "IPv6") <<
"(port=" << port << ")" <<
std::endl;
// broadcast/multicast address listup
auto addresses = listup_addresses(is_ipv6, port, false);
if (addresses.size() <= 0) {
std::cout << "no interface" << std::endl;
return;
}
// socket create & bind
auto sock = socket(!is_ipv6? AF_INET: AF_INET6, SOCK_DGRAM, 0);
ASSERT(sock != INVALID_SOCKET, "error: socket");
auto addr = sockaddr_holder::from(
!is_ipv6? AF_INET: AF_INET6, nullptr, port);
int reuseaddr = 1;
int result = setsockopt(
sock,
SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast<const char*>(&reuseaddr), sizeof(reuseaddr));
ASSERT(result == 0, "error: setsockopt");
result = bind(sock, addr.addr, addr.len);
ASSERT(result == 0, "error: bind");
if (!is_ipv6) {
// nothing
} else {
// add multicast membership option
for (size_t i = 0; i < addresses.size(); i++) {
auto mreq = ipv6_mreq();
mreq.ipv6mr_multiaddr = addresses[i].addr_in6->sin6_addr;
mreq.ipv6mr_interface = addresses[i].addr_in6->sin6_scope_id;
result = setsockopt(
sock,
IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
reinterpret_cast<const char*>(&mreq), sizeof(mreq));
ASSERT(result == 0, "error: setsockopt");
}
}
std::cout << "waiting..." << std::endl;
std::vector<char> buffer(2048);
while (true) {
auto fds = fd_set();
FD_ZERO(&fds);
FD_SET(sock, &fds);
auto tv = timeval();
tv.tv_sec = 0;
tv.tv_usec = 50*1000;
result = select(sock+1, &fds, nullptr, nullptr, &tv);
ASSERT(result != -1, "error: select");
if (FD_ISSET(sock, &fds)) {
// receive a udp packet
auto remote_addr = sockaddr_holder::from(
!is_ipv6? AF_INET: AF_INET6);
result = recvfrom(
sock,
&buffer[0],
static_cast<int>(buffer.size()*sizeof(char)),
0,
remote_addr.addr, &remote_addr.len);
ASSERT(result != -1, "error: recvfrom");
std::string received_data(
buffer.begin(),
buffer.begin()+result);
if (received_data == request_message) {
std::cout << "requested from " <<
remote_addr.to_string() <<
std::endl;
// send a response-packet
result = sendto(
sock,
response_message.c_str(),
static_cast<int>(response_message.size()*sizeof(char)),
0,
remote_addr.addr,
remote_addr.len);
ASSERT(result != -1, "error: sendto");
}
}
}
}
// client
void sample_client(
bool is_ipv6,
int port)
{
std::cout <<
"multicast-sample client for " <<
(!is_ipv6? "IPv4": "IPv6") <<
"(port=" << port << ")" <<
std::endl;
// broadcast/multicast address listup
auto addresses = listup_addresses(is_ipv6, port, true);
if (addresses.size() <= 0) {
std::cout << "no interface" << std::endl;
return;
}
// socket create & bind
auto sock = socket(!is_ipv6? AF_INET: AF_INET6, SOCK_DGRAM, 0);
ASSERT(sock != INVALID_SOCKET, "error: socket");
auto addr = sockaddr_holder::from(
!is_ipv6? AF_INET: AF_INET6);
int result = bind(sock, addr.addr, addr.len);
ASSERT(result == 0, "error: bind");
if (!is_ipv6) {
// set broadcast option
int broadcast = 1;
result = setsockopt(
sock,
SOL_SOCKET, SO_BROADCAST,
reinterpret_cast<const char*>(&broadcast), sizeof(broadcast));
ASSERT(result == 0, "error: setsockopt");
} else {
// add multicast membership option
for (size_t i = 0; i < addresses.size(); i++) {
auto mreq = ipv6_mreq();
mreq.ipv6mr_multiaddr = addresses[i].addr_in6->sin6_addr;
mreq.ipv6mr_interface = addresses[i].addr_in6->sin6_scope_id;
result = setsockopt(
sock,
IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
reinterpret_cast<const char*>(&mreq), sizeof(mreq));
ASSERT(result == 0, "error: setsockopt");
}
}
std::vector<char> buffer(2048);
DWORD t1 = GetTickCount()-1000;
while (true) {
DWORD t2 = GetTickCount();
if (t2-t1 >= 1000) {
std::cout << "sending..." << std::endl;
for (size_t i = 0; i < addresses.size(); i++) {
// send a request-packet
result = sendto(
sock,
request_message.c_str(),
static_cast<int>(request_message.size()*sizeof(char)),
0,
addresses[i].addr,
addresses[i].len);
ASSERT(result != -1, "error: sendto");
}
t1 = t2;
}
auto fds = fd_set();
FD_ZERO(&fds);
FD_SET(sock, &fds);
auto tv = timeval();
tv.tv_sec = 0;
tv.tv_usec = 50*1000;
result = select(sock+1, &fds, nullptr, nullptr, &tv);
ASSERT(result != -1, "error: select");
if (FD_ISSET(sock, &fds)) {
// receive a udp packet
auto remote_addr = sockaddr_holder::from(
!is_ipv6? AF_INET: AF_INET6);
result = recvfrom(
sock,
&buffer[0],
static_cast<int>(buffer.size())*sizeof(char),
0,
remote_addr.addr, &remote_addr.len);
ASSERT(result != -1, "error: recvfrom");
std::string received_data(
buffer.begin(),
buffer.begin()+result);
if (received_data == response_message) {
std::cout << "responsed from " <<
remote_addr.to_string() <<
std::endl;
}
}
}
}
// main
int main(
int argc,
char** argv)
{
bool server = true, is_ipv6 = true;
int port = default_port;
for (int i = 1; i < argc; i++) {
std::string arg = argv[i];
if (arg == "-s") {
server = true;
} else if (arg == "-c") {
server = false;
} else if (arg == "-6") {
is_ipv6 = true;
} else if (arg == "-4") {
is_ipv6 = false;
} else if (arg == "-p") {
if (i < argc-1) {
port = atoi(argv[++i]);
}
}
}
auto wsad = WSADATA();
int result = WSAStartup(MAKEWORD(2, 0), &wsad);
ASSERT(result == 0, "error: WSAStartup");
if (server) {
sample_server(is_ipv6, port);
} else {
sample_client(is_ipv6, port);
}
return 0;
}
// multicast sample program for C++ / Linux
#include <vector>
#include <set>
#include <string>
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <cstring>
#include <cstdint>
#include <ctime>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <net/if.h>
// utility
#define ASSERT(cnd, msg) \
do { \
if (!(cnd)) { \
std::cout << (msg) << std::endl; \
std::exit(1); \
} \
} \
while (0)
class sockaddr_holder {
public:
union {
sockaddr* addr;
sockaddr_in* addr_in;
sockaddr_in6* addr_in6;
};
socklen_t len;
sockaddr_holder() :
addr(nullptr),
len(0)
{
}
sockaddr_holder(
const sockaddr_holder& other) :
addr(nullptr),
len(0)
{
assign(other.addr, other.len);
}
sockaddr_holder(
sockaddr_holder&& other) :
addr(nullptr),
len(0)
{
addr = other.addr;
len = other.len;
other.addr = nullptr;
other.len = 0;
}
sockaddr_holder(
sockaddr* new_addr,
socklen_t new_len) :
addr(nullptr),
len(0)
{
assign(new_addr, new_len);
}
~sockaddr_holder() {
assign(nullptr, 0);
}
static sockaddr_holder from(
int family,
const void* addr_bytes = nullptr,
int port = 0,
int scope_id = 0)
{
sockaddr_holder addr(
nullptr,
(family == AF_INET)?
sizeof(sockaddr_in):
(family == AF_INET6)?
sizeof(sockaddr_in6):
// else
size_t(0));
addr.addr->sa_family = family;
if (family == AF_INET) {
if (addr_bytes != nullptr) {
memcpy(&addr.addr_in->sin_addr, addr_bytes, 4);
}
addr.addr_in->sin_port = htons(static_cast<std::uint16_t>(port));
} else if (family == AF_INET6) {
if (addr_bytes != nullptr) {
memcpy(&addr.addr_in6->sin6_addr, addr_bytes, 16);
}
addr.addr_in6->sin6_port = htons(static_cast<std::uint16_t>(port));
addr.addr_in6->sin6_scope_id = scope_id;
}
return addr;
}
static sockaddr_holder from_str(
int family,
const char* addr_str,
int port = 0,
int scope_id = 0)
{
std::uint8_t addr_bytes[16];
int result = inet_pton(
family,
addr_str,
&addr_bytes);
ASSERT(result == 1, "error: inet_pton");
return from(family, addr_bytes, port, scope_id);
}
sockaddr_holder& operator=(
const sockaddr_holder& other)
{
if (&other != this) {
assign(other.addr, other.len);
}
return *this;
}
sockaddr_holder& operator=(
sockaddr_holder&& other)
{
if (&other != this) {
addr = other.addr;
len = other.len;
other.addr = nullptr;
other.len = 0;
}
return *this;
}
std::string to_string() const {
std::stringstream stream;
std::vector<char> nbuf(256);
if (addr == nullptr) {
stream << "null";
} else if (addr->sa_family == AF_INET) {
stream <<
inet_ntop(
addr_in->sin_family, &addr_in->sin_addr,
&nbuf[0], nbuf.size()) <<
':' <<
ntohs(addr_in->sin_port);
} else if (addr->sa_family == AF_INET6) {
stream <<
'[' <<
inet_ntop(
addr_in->sin_family, &addr_in6->sin6_addr,
&nbuf[0], nbuf.size()) <<
'%' <<
addr_in6->sin6_scope_id <<
"]:" <<
ntohs(addr_in6->sin6_port);
}
return stream.str();
}
private:
void assign(
sockaddr* new_addr,
socklen_t new_len)
{
if (addr != nullptr) {
std::free(addr);
addr = nullptr;
len = 0;
}
if (new_len > 0) {
addr = reinterpret_cast<sockaddr*>(std::malloc(new_len));
ASSERT(addr != nullptr, "error: malloc");
len = new_len;
if (new_addr != nullptr) {
std::memcpy(addr, new_addr, new_len);
} else {
std::memset(addr, 0, new_len);
}
}
}
};
// constants
const int default_port = 50000;
sockaddr_holder multicast_address =
sockaddr_holder::from_str(AF_INET6, "ff02::1");
const std::string request_message =
"Are you a MulticastSample server?";
const std::string response_message =
"Yes, I am a MulticastSample server!";
// common
std::vector<sockaddr_holder> listup_addresses(
bool is_ipv6,
int port,
bool strict)
{
std::vector<sockaddr_holder> addresses;
ifaddrs* ifas = nullptr;
int result = getifaddrs(&ifas);
ASSERT(result != -1, "error: getifaddrs");
std::set<std::uint32_t> listed;
if (!is_ipv6) {
for (auto ifa = ifas; ifa != nullptr; ifa = ifa->ifa_next) {
if (((ifa->ifa_flags & IFF_POINTOPOINT) != 0) ||
((ifa->ifa_flags & IFF_LOOPBACK) != 0) ||
((ifa->ifa_flags & IFF_BROADCAST) == 0) ||
(ifa->ifa_addr == nullptr) ||
(ifa->ifa_addr->sa_family != AF_INET) ||
(ifa->ifa_broadaddr == nullptr) ||
(ifa->ifa_broadaddr->sa_family != AF_INET))
{
continue;
}
if (strict &&
((ifa->ifa_flags & IFF_UP) == 0))
{
continue;
}
sockaddr_holder baddr(
ifa->ifa_broadaddr,
sizeof(sockaddr_in));
auto addr_val = baddr.addr_in->sin_addr.s_addr;
if (listed.find(addr_val) != listed.end()) {
continue;
}
auto addr = sockaddr_holder::from(AF_INET, &addr_val, port);
addresses.push_back(addr);
listed.insert(addr_val);
}
} else {
for (auto ifa = ifas; ifa != nullptr; ifa = ifa->ifa_next) {
if (((ifa->ifa_flags & IFF_POINTOPOINT) != 0) ||
((ifa->ifa_flags & IFF_LOOPBACK) != 0) ||
((ifa->ifa_flags & IFF_BROADCAST) == 0) ||
(ifa->ifa_addr == nullptr) ||
(ifa->ifa_addr->sa_family != AF_INET6))
{
continue;
}
if (strict &&
((ifa->ifa_flags & IFF_UP) == 0))
{
continue;
}
sockaddr_holder uaddr(
ifa->ifa_addr,
sizeof(sockaddr_in6));
auto scope_id = uaddr.addr_in6->sin6_scope_id;
if (listed.find(scope_id) != listed.end()) {
continue;
}
auto addr = sockaddr_holder::from(
AF_INET6,
&multicast_address.addr_in6->sin6_addr, port, scope_id);
addresses.push_back(addr);
listed.insert(scope_id);
}
}
freeifaddrs(ifas);
return addresses;
}
// server
void sample_server(
bool is_ipv6,
int port)
{
std::cout <<
"multicast-sample server for " <<
(!is_ipv6? "IPv4": "IPv6") <<
"(port=" << port << ")" <<
std::endl;
// broadcast/multicast address listup
auto addresses = listup_addresses(is_ipv6, port, false);
if (addresses.size() <= 0) {
std::cout << "no interface" << std::endl;
return;
}
// socket create & bind
auto sock = socket(!is_ipv6? AF_INET: AF_INET6, SOCK_DGRAM, 0);
ASSERT(sock != -1, "error: socket");
auto addr = sockaddr_holder::from(
!is_ipv6? AF_INET: AF_INET6, nullptr, port);
int reuseaddr = 1;
int result = setsockopt(
sock,
SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast<const char*>(&reuseaddr), sizeof(reuseaddr));
ASSERT(result == 0, "error: setsockopt");
result = bind(sock, addr.addr, addr.len);
ASSERT(result == 0, "error: bind");
if (!is_ipv6) {
// nothing
} else {
// add multicast membership option
for (size_t i = 0; i < addresses.size(); i++) {
auto mreq = ipv6_mreq();
mreq.ipv6mr_multiaddr = addresses[i].addr_in6->sin6_addr;
mreq.ipv6mr_interface = addresses[i].addr_in6->sin6_scope_id;
result = setsockopt(
sock,
IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
reinterpret_cast<const char*>(&mreq), sizeof(mreq));
ASSERT(result == 0, "error: setsockopt");
}
}
std::cout << "waiting..." << std::endl;
std::vector<char> buffer(2048);
while (true) {
auto fds = fd_set();
FD_ZERO(&fds);
FD_SET(sock, &fds);
auto tv = timeval();
tv.tv_sec = 0;
tv.tv_usec = 50*1000;
result = select(sock+1, &fds, nullptr, nullptr, &tv);
ASSERT(result != -1, "error: select");
if (FD_ISSET(sock, &fds)) {
// receive a udp packet
auto remote_addr = sockaddr_holder::from(
!is_ipv6? AF_INET: AF_INET6);
result = recvfrom(
sock,
&buffer[0],
static_cast<int>(buffer.size()*sizeof(char)),
0,
remote_addr.addr, &remote_addr.len);
ASSERT(result != -1, "error: recvfrom");
std::string received_data(
buffer.begin(),
buffer.begin()+result);
if (received_data == request_message) {
std::cout << "requested from " <<
remote_addr.to_string() <<
std::endl;
// send a response-packet
result = sendto(
sock,
response_message.c_str(),
static_cast<int>(response_message.size()*sizeof(char)),
0,
remote_addr.addr,
remote_addr.len);
ASSERT(result != -1, "error: sendto");
}
}
}
}
// client
void sample_client(
bool is_ipv6,
int port)
{
std::cout <<
"multicast-sample client for " <<
(!is_ipv6? "IPv4": "IPv6") <<
"(port=" << port << ")" <<
std::endl;
// broadcast/multicast address listup
auto addresses = listup_addresses(is_ipv6, port, true);
if (addresses.size() <= 0) {
std::cout << "no interface" << std::endl;
return;
}
// socket create & bind
auto sock = socket(!is_ipv6? AF_INET: AF_INET6, SOCK_DGRAM, 0);
ASSERT(sock != -1, "error: socket");
auto addr = sockaddr_holder::from(
!is_ipv6? AF_INET: AF_INET6);
int result = bind(sock, addr.addr, addr.len);
ASSERT(result == 0, "error: bind");
if (!is_ipv6) {
// set broadcast option
int broadcast = 1;
result = setsockopt(
sock,
SOL_SOCKET, SO_BROADCAST,
reinterpret_cast<const char*>(&broadcast), sizeof(broadcast));
ASSERT(result == 0, "error: setsockopt");
} else {
// add multicast membership option
for (size_t i = 0; i < addresses.size(); i++) {
auto mreq = ipv6_mreq();
mreq.ipv6mr_multiaddr = addresses[i].addr_in6->sin6_addr;
mreq.ipv6mr_interface = addresses[i].addr_in6->sin6_scope_id;
result = setsockopt(
sock,
IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
reinterpret_cast<const char*>(&mreq), sizeof(mreq));
ASSERT(result == 0, "error: setsockopt");
}
}
std::vector<char> buffer(2048);
auto t1 = timeval();
result = gettimeofday(&t1, nullptr);
ASSERT(result != -1, "error: gettimeofday");
t1.tv_sec--;
while (true) {
auto t2 = timeval();
result = gettimeofday(&t2, nullptr);
ASSERT(result != -1, "error: gettimeofday");
if ((t2.tv_sec-t1.tv_sec >= 2) ||
((t2.tv_sec-t1.tv_sec == 1) &&
(t2.tv_usec >= t1.tv_usec)))
{
std::cout << "sending..." << std::endl;
for (size_t i = 0; i < addresses.size(); i++) {
// send a request-packet
result = sendto(
sock,
request_message.c_str(),
static_cast<int>(request_message.size()*sizeof(char)),
0,
addresses[i].addr,
addresses[i].len);
ASSERT(result != -1, "error: sendto");
}
t1 = t2;
}
auto fds = fd_set();
FD_ZERO(&fds);
FD_SET(sock, &fds);
auto tv = timeval();
tv.tv_sec = 0;
tv.tv_usec = 50*1000;
result = select(sock+1, &fds, nullptr, nullptr, &tv);
ASSERT(result != -1, "error: select");
if (FD_ISSET(sock, &fds)) {
// receive a udp packet
auto remote_addr = sockaddr_holder::from(
!is_ipv6? AF_INET: AF_INET6);
result = recvfrom(
sock,
&buffer[0],
static_cast<int>(buffer.size())*sizeof(char),
0,
remote_addr.addr, &remote_addr.len);
ASSERT(result != -1, "error: recvfrom");
std::string received_data(
buffer.begin(),
buffer.begin()+result);
if (received_data == response_message) {
std::cout << "responsed from " <<
remote_addr.to_string() <<
std::endl;
}
}
}
}
// main
int main(
int argc,
char** argv)
{
bool server = true, is_ipv6 = true;
int port = default_port;
for (int i = 1; i < argc; i++) {
std::string arg = argv[i];
if (arg == "-s") {
server = true;
} else if (arg == "-c") {
server = false;
} else if (arg == "-6") {
is_ipv6 = true;
} else if (arg == "-4") {
is_ipv6 = false;
} else if (arg == "-p") {
if (i < argc-1) {
port = atoi(argv[++i]);
}
}
}
if (server) {
sample_server(is_ipv6, port);
} else {
sample_client(is_ipv6, port);
}
return 0;
}
// multicast sample program for C# / .NET Framework or .NET Core
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
namespace MulticastSample {
class Program {
// constants
const int DefaultPort = 50000;
static readonly IPAddress MulticastAddress =
IPAddress.Parse("ff02::1");
static readonly byte[] RequestMessage =
Encoding.ASCII.GetBytes("Are you a MulticastSample server?");
static readonly byte[] ResponseMessage =
Encoding.ASCII.GetBytes("Yes, I am a MulticastSample server!");
// common
static List<IPEndPoint> ListupAddresses(
bool isIPv6,
int port,
bool strict)
{
var addresses = new List<IPEndPoint>();
var adapters = NetworkInterface.GetAllNetworkInterfaces();
var listed = new Dictionary<uint, bool>();
if (!isIPv6) {
foreach (var adapter in adapters) {
var properties = adapter.GetIPProperties();
if (!adapter.Supports(NetworkInterfaceComponent.IPv4) ||
(adapter.NetworkInterfaceType == NetworkInterfaceType.Loopback) ||
(adapter.NetworkInterfaceType == NetworkInterfaceType.Ppp))
{
continue;
}
if (strict &&
(adapter.OperationalStatus != OperationalStatus.Up))
{
continue;
}
foreach (var unicast in properties.UnicastAddresses) {
if (unicast.Address.AddressFamily != AddressFamily.InterNetwork) {
continue;
}
byte[] addressBytes = unicast.Address.GetAddressBytes(),
maskBytes = unicast.IPv4Mask.GetAddressBytes();
if (addressBytes.Length != maskBytes.Length) {
continue;
}
uint addrVal = 0;
for (int i = 0; i < addressBytes.Length; i++) {
addressBytes[i] |= (byte)(~maskBytes[i]);
addrVal = (addrVal << 8) | addressBytes[i];
}
if (listed.ContainsKey(addrVal)) {
continue;
}
var addr = new IPAddress(addressBytes);
addresses.Add(new IPEndPoint(addr, port));
listed[addrVal] = true;
}
}
} else {
foreach (var adapter in adapters) {
var properties = adapter.GetIPProperties();
if (!adapter.Supports(NetworkInterfaceComponent.IPv6) ||
!adapter.SupportsMulticast ||
(adapter.NetworkInterfaceType == NetworkInterfaceType.Loopback) ||
(adapter.NetworkInterfaceType == NetworkInterfaceType.Ppp))
{
continue;
}
if (strict &&
(adapter.OperationalStatus != OperationalStatus.Up))
{
continue;
}
bool hasIPv6Address = false;
foreach (var unicast in properties.UnicastAddresses) {
if (unicast.Address.AddressFamily == AddressFamily.InterNetworkV6) {
hasIPv6Address = true;
break;
}
}
if (!hasIPv6Address) {
continue;
}
int scopeId = properties.GetIPv6Properties().Index;
if (listed.ContainsKey((uint)scopeId)) {
continue;
}
addresses.Add(
new IPEndPoint(
new IPAddress(
MulticastAddress.GetAddressBytes(),
scopeId),
port));
listed[(uint)scopeId] = true;
}
}
return addresses;
}
// server
static void SampleServer(
bool isIPv6,
int port)
{
Console.WriteLine(
"multicast-sample server for {0}(port={1})",
!isIPv6? "IPv4": "IPv6",
port);
// broadcast/multicast address listup
var addresses = ListupAddresses(isIPv6, port, false);
if (addresses.Count <= 0) {
Console.WriteLine("no interface");
return;
}
// socket create & bind
var sock = new Socket(
!isIPv6?
AddressFamily.InterNetwork:
AddressFamily.InterNetworkV6,
SocketType.Dgram,
ProtocolType.IP);
sock.SetSocketOption(
SocketOptionLevel.Socket,
SocketOptionName.ReuseAddress,
true);
sock.Bind(
new IPEndPoint(
!isIPv6?
IPAddress.Any:
IPAddress.IPv6Any,
port));
if (!isIPv6) {
// nothing
} else {
// add multicast membership option
foreach (var address in addresses) {
var opt = new IPv6MulticastOption(
address.Address,
address.Address.ScopeId);
sock.SetSocketOption(
SocketOptionLevel.IPv6,
SocketOptionName.AddMembership,
opt);
}
}
Console.WriteLine("waiting...");
byte[] buffer = new byte[2048];
while (true) {
var sockets = new List<Socket>(new [] {sock});
Socket.Select(sockets, null, null, 50);
if (sockets.Count > 0) {
// receive a udp packet
EndPoint remoteAddr = new IPEndPoint(
!isIPv6?
IPAddress.Any:
IPAddress.IPv6Any,
0);
int receivedLength = sock.ReceiveFrom(buffer, ref remoteAddr);
byte[] receivedData = new byte[receivedLength];
Array.Copy(buffer, receivedData, receivedLength);
if (receivedData.SequenceEqual(RequestMessage)) {
Console.WriteLine("requested from {0}", remoteAddr);
// send a response-packet
sock.SendTo(ResponseMessage, remoteAddr);
}
}
}
}
// client
static void SampleClient(
bool isIPv6,
int port)
{
Console.WriteLine(
"multicast-sample client for {0}(port={1})",
!isIPv6? "IPv4": "IPv6",
port);
// broadcast/multicast address listup
var addresses = ListupAddresses(isIPv6, port, true);
if (addresses.Count <= 0) {
Console.WriteLine("no interface");
return;
}
// socket create & bind
var sock = new Socket(
!isIPv6?
AddressFamily.InterNetwork:
AddressFamily.InterNetworkV6,
SocketType.Dgram,
ProtocolType.IP);
sock.Bind(
new IPEndPoint(
!isIPv6?
IPAddress.Any:
IPAddress.IPv6Any,
0));
if (!isIPv6) {
// set broadcast option
sock.SetSocketOption(
SocketOptionLevel.Socket,
SocketOptionName.Broadcast,
true);
} else {
// add multicast membership option
foreach (var address in addresses) {
var opt = new IPv6MulticastOption(
address.Address,
address.Address.ScopeId);
sock.SetSocketOption(
SocketOptionLevel.IPv6,
SocketOptionName.AddMembership,
opt);
}
}
byte[] buffer = new byte[2048];
var sw = new Stopwatch();
while (true) {
if (!sw.IsRunning ||
(sw.ElapsedMilliseconds >= 1000))
{
Console.WriteLine("sending...");
foreach (var address in addresses) {
// send a request-packet
sock.SendTo(RequestMessage, address);
}
sw.Restart();
}
var sockets = new List<Socket>(new [] {sock});
Socket.Select(sockets, null, null, 50);
if (sockets.Count > 0) {
// receive a udp packet
EndPoint remoteAddr = new IPEndPoint(
!isIPv6?
IPAddress.Any:
IPAddress.IPv6Any,
0);
int receivedLength = sock.ReceiveFrom(buffer, ref remoteAddr);
byte[] receivedData = new byte[receivedLength];
Array.Copy(buffer, receivedData, receivedLength);
if (receivedData.SequenceEqual(ResponseMessage)) {
Console.WriteLine("responsed from {0}", remoteAddr);
}
}
}
}
// main
static void Main(
string[] args)
{
bool server = true, isIPv6 = true;
int port = DefaultPort;
for (int i = 0; i < args.Length; i++) {
if (args[i] == "-c") {
server = false;
} else if (args[i] == "-s") {
server = true;
} else if (args[i] == "-4") {
isIPv6 = false;
} else if (args[i] == "-6") {
isIPv6 = true;
} else if (args[i] == "-p") {
if (i < args.Length-1) {
port = int.Parse(args[++i]);
}
}
}
if (server) {
SampleServer(isIPv6, port);
} else {
SampleClient(isIPv6, port);
}
}
}
}
// multicast sample program for Java
package multicastsample;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketAddress;
import java.net.StandardProtocolFamily;
import java.net.StandardSocketOptions;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.spi.SelectorProvider;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashSet;
public class MulticastSample {
// utility
static InetAddress addressFromName(
String name)
{
InetAddress addr;
try {
addr = InetAddress.getByName(name);
} catch (Exception e) {
addr = null;
}
return addr;
}
// constant
static final int defaultPort = 50000;
static final InetAddress multicastAddress =
addressFromName("ff02::1");
static final byte[] requestMessage =
"Are you a MulticastSample server?".getBytes(
StandardCharsets.UTF_8);
static final byte[] responseMessage =
"Yes, I am a MulticastSample server!".getBytes(
StandardCharsets.UTF_8);
// common
static ArrayList<InetSocketAddress> listupAddresses(
boolean isIPv6,
int port,
boolean strict)
throws Exception
{
ArrayList<InetSocketAddress> addresses = new ArrayList<>();
Enumeration<NetworkInterface> adapters =
NetworkInterface.getNetworkInterfaces();
HashSet<Integer> listed = new HashSet<>();
if (!isIPv6) {
while (adapters.hasMoreElements()) {
NetworkInterface adapter = adapters.nextElement();
if (adapter.isLoopback() ||
adapter.isPointToPoint())
{
continue;
}
if (strict &&
!adapter.isUp())
{
continue;
}
for (InterfaceAddress iaddr : adapter.getInterfaceAddresses()) {
InetAddress addr = iaddr.getBroadcast();
if ((addr == null) ||
!(addr instanceof Inet4Address))
{
continue;
}
int addrVal = 0;
for (byte bt : addr.getAddress()) {
addrVal = (addrVal << 8) | (bt & 0xFF);
}
if (listed.contains(addrVal)) {
continue;
}
addresses.add(new InetSocketAddress(addr, port));
listed.add(addrVal);
}
}
} else {
while (adapters.hasMoreElements()) {
NetworkInterface adapter = adapters.nextElement();
if (adapter.isLoopback() ||
adapter.isPointToPoint())
{
continue;
}
if (strict &&
!adapter.isUp())
{
continue;
}
boolean hasIPv6Address = false;
for (InterfaceAddress iaddr : adapter.getInterfaceAddresses()) {
InetAddress addr = iaddr.getBroadcast();
if ((addr != null) &&
!(addr instanceof Inet6Address))
{
hasIPv6Address = true;
break;
}
}
if (!hasIPv6Address) {
continue;
}
int scopeId = adapter.getIndex();
if (listed.contains(scopeId)) {
continue;
}
addresses.add(
new InetSocketAddress(
Inet6Address.getByAddress(
null, multicastAddress.getAddress(), adapter),
port));
listed.add(scopeId);
}
}
return addresses;
}
// server
static void sampleServer(
boolean isIPv6,
int port)
throws Exception
{
System.out.printf(
"multicast-sample server for %s(port=%d)%n",
!isIPv6? "IPv4": "IPv6",
port);
// broadcast/multicast address listup
ArrayList<InetSocketAddress> addresses =
listupAddresses(isIPv6, port, false);
if (addresses.size() <= 0) {
System.out.println("no interface");
return;
}
// socket create & bind
DatagramChannel sock = DatagramChannel.open(
!isIPv6?
StandardProtocolFamily.INET:
StandardProtocolFamily.INET6);
sock.setOption(StandardSocketOptions.SO_REUSEADDR, true);
sock.bind(
new InetSocketAddress(port));
if (!isIPv6) {
// nothing
} else {
// add multicast membership option
for (InetSocketAddress address : addresses) {
Inet6Address addr = (Inet6Address)address.getAddress();
sock.join(addr, addr.getScopedInterface());
}
}
System.out.println("waiting...");
sock.configureBlocking(false);
Selector selector = SelectorProvider.provider().openSelector();
sock.register(selector, SelectionKey.OP_READ);
while (true) {
if (selector.select(50) > 0) {
selector.selectedKeys().clear();
// receive a udp packet
ByteBuffer recvBuffer = ByteBuffer.allocate(2048);
//recvBuffer.clear();
SocketAddress remoteAddr = sock.receive(recvBuffer);
byte[] receivedData = new byte[recvBuffer.position()];
recvBuffer.flip();
recvBuffer.get(receivedData);
if (Arrays.equals(receivedData, requestMessage)) {
System.out.printf("requested from %s%n", remoteAddr.toString());
// send a response-packet
ByteBuffer sendBuffer = ByteBuffer.wrap(responseMessage);
sock.send(sendBuffer, remoteAddr);
}
sock.register(selector, SelectionKey.OP_READ);
}
}
}
// client
static void sampleClient(
boolean isIPv6,
int port)
throws Exception
{
System.out.printf(
"multicast-sample client for %s(port=%d)%n",
!isIPv6? "IPv4": "IPv6",
port);
// broadcast/multicast address listup
ArrayList<InetSocketAddress> addresses =
listupAddresses(isIPv6, port, false);
if (addresses.size() <= 0) {
System.out.println("no interface");
return;
}
// socket create & bind
DatagramChannel sock = DatagramChannel.open(
!isIPv6?
StandardProtocolFamily.INET:
StandardProtocolFamily.INET6);
sock.bind(
new InetSocketAddress(0));
if (!isIPv6) {
// set broadcast option
sock.setOption(StandardSocketOptions.SO_BROADCAST, true);
} else {
// add multicast membership option
for (InetSocketAddress address : addresses) {
Inet6Address addr = (Inet6Address)address.getAddress();
sock.join(addr, addr.getScopedInterface());
}
}
sock.configureBlocking(false);
Selector selector = SelectorProvider.provider().openSelector();
sock.register(selector, SelectionKey.OP_READ);
long t1 = System.currentTimeMillis()-1000;
while (true) {
long t2 = System.currentTimeMillis();
if (t2-t1 >= 1000) {
System.out.println("sending...");
for (InetSocketAddress address : addresses) {
// send a request-packet
ByteBuffer sendBuffer = ByteBuffer.wrap(requestMessage);
sock.send(sendBuffer, address);
}
t1 = t2;
}
if (selector.select(50) > 0) {
selector.selectedKeys().clear();
// receive a udp packet
ByteBuffer recvBuffer = ByteBuffer.allocate(2048);
//recvBuffer.clear();
SocketAddress remoteAddr = sock.receive(recvBuffer);
byte[] receivedData = new byte[recvBuffer.position()];
recvBuffer.flip();
recvBuffer.get(receivedData);
if (Arrays.equals(receivedData, responseMessage)) {
System.out.printf("responsed from %s%n", remoteAddr.toString());
}
sock.register(selector, SelectionKey.OP_READ);
}
}
}
// main
public static void main(
String[] args)
{
try {
boolean server = true, isIPv6 = true;
int port = defaultPort;
for (int i = 0; i < args.length; i++) {
if (args[i].equals("-c")) {
server = false;
} else if (args[i].equals("-s")) {
server = true;
} else if (args[i].equals("-4")) {
isIPv6 = false;
} else if (args[i].equals("-6")) {
isIPv6 = true;
} else if (args[i].equals("-p")) {
if (i < args.length-1) {
port = Integer.decode(args[++i]);
}
}
}
if (server) {
sampleServer(isIPv6, port);
} else {
sampleClient(isIPv6, port);
}
} catch (Exception e) {
System.out.println(e);
}
}
}