Japanese
Manuke Station : Tools & Data : MulticastSample
English Page

IPv4ブロードキャストをIPv6マルチキャストで代替する

◎概要

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