English
Manuke Station : Tools & Data : MulticastSample
Japanese Page

How to substitute IPv4 broadcast for IPv6 multicast

ABOUT:

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.

POINT:

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.

USAGE:

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)

OPTIONS:

-s server mode(default)
-c client mode
-6 IPv6 mode(default)
-4 IPv4 mode
-p <num> TCP/IP port number(default : 50000)

SAMPLE CODE:

// 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); } } }