When we want to send UDP packets to all devices in a network, we will use broadcast addresses (all bits of the host part is 1) in IPv4.
On the other hand, there are no broadcast addresses in IPv6. When we want to take a similar step in IPv6, we will use a multicast address ff02::1 to execute multicast to all link-local nodes.
Unlike IPv4 broadcast, IPv6 multicast must specify network interfaces.
When we want to execute IPv6 multicast with all network interfaces, similar to IPv4 broadcast, we must enumerate network interfaces.
Because enumeration of network interfaces is not standardized, these implementation methods vary according to OSs / frameworks.
The following sample codes are implemented the nearly same processing by C++(Windows version and Linux version), C#, and Java.
The sample code MulticastSample works in server mode or client mode.
When there are machines executing server mode MulticastSample in the same network, client mode MulticastSample can enumerate the IP addresses.
In addition, MulticastSample can test the nealy same behavior in both IPv4 and IPv6.(but, IPv4 mode and the IPv6 mode cannot communicate each other)
-s | server mode(default) |
-c | client mode |
-6 | IPv6 mode(default) |
-4 | IPv4 mode |
-p <num> | TCP/IP port number(default : 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);
}
}
}