Skip to content

Instantly share code, notes, and snippets.

@Tosainu
Last active May 23, 2021 08:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Tosainu/ec4d2cd799e8e32548c529de1dcfc775 to your computer and use it in GitHub Desktop.
Save Tosainu/ec4d2cd799e8e32548c529de1dcfc775 to your computer and use it in GitHub Desktop.
複数インターフェイス環境でのマルチキャスト通信テスト
#include <iostream>
#include <map>
#include <memory>
#include <string_view>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/lexical_cast.hpp>
using namespace std::chrono_literals;
using namespace std::string_literals;
auto main(int argc, char** argv) -> int {
if (argc != 4) return -1;
const auto addr = boost::asio::ip::make_address_v4(argv[1]);
const auto port = boost::lexical_cast<unsigned short>(argv[2]);
const auto name = std::string{argv[3]};
boost::asio::io_context ctx{1};
boost::asio::ip::udp::endpoint listen_ep{boost::asio::ip::udp::v4(), port};
boost::asio::ip::udp::endpoint mcast_ep{addr, port};
boost::asio::ip::udp::socket sock{ctx, listen_ep.protocol()};
sock.set_option(boost::asio::ip::udp::socket::reuse_address(true));
sock.bind(listen_ep);
sock.set_option(boost::asio::ip::multicast::enable_loopback(false));
sock.set_option(boost::asio::ip::multicast::join_group(addr));
boost::asio::steady_timer timer{ctx};
boost::asio::spawn(ctx, [&sock, &mcast_ep, &timer, &name](auto&& yield) {
for (;;) {
sock.async_send_to(boost::asio::buffer("I'm "s + name + '!'), mcast_ep, yield);
timer.expires_after(1s);
timer.async_wait(yield);
}
});
boost::asio::spawn(ctx, [&sock](auto&& yield) {
char buf[1024];
boost::asio::ip::udp::endpoint sender_ep{};
for (;;) {
const auto len = sock.async_receive_from(boost::asio::buffer(buf), sender_ep, yield);
std::cout << sender_ep.address() << "> " << std::string_view{buf, len} << std::endl;
}
});
ctx.run();
}
version: "3"
services:
# Gist に置けるようにするため build context を全て同じディレクトリにしている
# build context を不必要に広く取るのはよろしくないので普段はやめるべき
server:
build:
context: .
args:
src: server.cc
bin: &server-bin /server
command: [*server-bin, &mcast-addr "224.5.0.1", &mcast-port "20001"]
networks:
net1:
net2:
net3:
client-1:
build: &build-client
context: .
args:
src: client.cc
bin: &client-bin /client
command: [*client-bin, *mcast-addr, *mcast-port, "client-1"]
networks:
net1:
client-2:
build: *build-client
command: [*client-bin, *mcast-addr, *mcast-port, "client-2"]
networks:
net2:
client-3:
build: *build-client
command: [*client-bin, *mcast-addr, *mcast-port, "client-3"]
networks:
net3:
networks:
net1:
net2:
net3:
FROM alpine as builder
RUN apk add boost-dev g++
ARG src
ARG bin
COPY "${src}" /
RUN g++ -Wall -Wextra -Werror -pedantic-errors -std=c++17 -O3 /${src} -o /${bin} -lboost_coroutine
FROM alpine
RUN apk add --no-cache libstdc++ boost-coroutine
ARG bin
COPY --from=builder /${bin} /${bin}
#include <iostream>
#include <map>
#include <memory>
#include <string_view>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/lexical_cast.hpp>
extern "C" {
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <sys/types.h>
}
using namespace std::chrono_literals;
using namespace std::string_literals;
auto main(int argc, char** argv) -> int {
if (argc != 3) return -1;
const auto port = boost::lexical_cast<unsigned short>(argv[2]);
// join_group, outbound_interface に設定してみる IP アドレスのリスト
std::map<std::string, boost::asio::ip::address_v4> ifaddrs{};
ifaddrs.emplace("address_v4::any", boost::asio::ip::address_v4::any());
// ネットワークインターフェイスと設定された IPv4 アドレスの列挙
{
::ifaddrs* ifa_head;
if (::getifaddrs(&ifa_head) != 0) return -1;
for (auto ifa = ifa_head; ifa; ifa = ifa->ifa_next) {
if (ifa->ifa_addr->sa_family != AF_INET) continue;
if (std::string_view{ifa->ifa_name} == "lo") continue;
const auto addr_native = reinterpret_cast<::sockaddr_in*>(ifa->ifa_addr);
const auto addr = ::ntohl(addr_native->sin_addr.s_addr);
ifaddrs.emplace(ifa->ifa_name, addr);
}
::freeifaddrs(ifa_head);
}
boost::asio::io_context ctx{1};
boost::asio::ip::udp::endpoint listen_ep{boost::asio::ip::udp::v4(), port};
boost::asio::ip::udp::endpoint mcast_ep{boost::asio::ip::make_address_v4(argv[1]), port};
boost::asio::ip::udp::socket sock{ctx, listen_ep.protocol()};
sock.set_option(boost::asio::ip::udp::socket::reuse_address(true));
sock.bind(listen_ep);
sock.set_option(boost::asio::ip::multicast::enable_loopback(false));
boost::asio::steady_timer timer{ctx};
boost::asio::spawn(ctx, [&sock, mcast_ep, &ifaddrs, &timer](auto&& yield) {
for (;;) {
for (const auto& [name, ifaddr] : ifaddrs) {
std::cout << "changing comm i/f: " << name << " (" << ifaddr << ')' << std::endl;
sock.set_option(boost::asio::ip::multicast::join_group(mcast_ep.address().to_v4(), ifaddr));
sock.set_option(boost::asio::ip::multicast::outbound_interface(ifaddr));
for (auto i = 0u; i < 5u; ++i) {
sock.async_send_to(boost::asio::buffer("Hello!"s), mcast_ep, yield);
timer.expires_after(1s);
timer.async_wait(yield);
}
sock.set_option(
boost::asio::ip::multicast::leave_group(mcast_ep.address().to_v4(), ifaddr));
}
}
});
boost::asio::spawn(ctx, [&sock](auto&& yield) {
char buf[1024];
boost::asio::ip::udp::endpoint sender_ep{};
for (;;) {
const auto len = sock.async_receive_from(boost::asio::buffer(buf), sender_ep, yield);
std::cout << sender_ep.address() << "> " << std::string_view{buf, len} << std::endl;
}
});
ctx.run();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment