112 lines
3.2 KiB
C++
112 lines
3.2 KiB
C++
/*
|
|
* This is a real-world example used to debug a multicast issue.
|
|
*
|
|
* After sending an initial small packet to the --multicast-address,
|
|
* this program will listen for packets on that multicast address,
|
|
* and reply to each incoming packet with a small burst of reply
|
|
* packets to the sender.
|
|
*
|
|
* It is included to serve as example for how to write UDP-based
|
|
* code using uv-co, including interaction with multicast features.
|
|
*/
|
|
|
|
#include <boost/program_options.hpp>
|
|
#include <boost/program_options/detail/parsers.hpp>
|
|
#include <boost/program_options/options_description.hpp>
|
|
#include <boost/program_options/value_semantic.hpp>
|
|
#include <boost/program_options/variables_map.hpp>
|
|
#include <fmt/core.h>
|
|
#include <fmt/format.h>
|
|
|
|
#include "uvco/exception.h"
|
|
#include "uvco/name_resolution.h"
|
|
#include "uvco/promise/promise.h"
|
|
#include "uvco/run.h"
|
|
#include "uvco/timer.h"
|
|
#include "uvco/udp.h"
|
|
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <iostream>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <uv.h>
|
|
#include <vector>
|
|
|
|
using namespace uvco;
|
|
|
|
struct Options {
|
|
const Loop &loop;
|
|
|
|
std::string listenAddress = "ff02::fb%wlp1s0";
|
|
std::string multicastAddress = "ff02::fb";
|
|
uint16_t port = 5353;
|
|
};
|
|
|
|
Options parseOptions(const Loop &loop, int argc, const char **argv) {
|
|
namespace po = boost::program_options;
|
|
Options options{loop};
|
|
bool help = false;
|
|
po::options_description desc;
|
|
desc.add_options()("listen-address",
|
|
po::value<std::string>(&options.listenAddress),
|
|
"Listen/connect address")(
|
|
"multicast-address", po::value<std::string>(&options.multicastAddress),
|
|
"Listen/connect address")("port", po::value<uint16_t>(&options.port),
|
|
"Listen/connect port")(
|
|
"help,h", po::bool_switch(&help), "Display help");
|
|
po::variables_map vm;
|
|
po::store(po::parse_command_line(argc, argv, desc), vm);
|
|
po::notify(vm);
|
|
if (help) {
|
|
std::cerr << desc << '\n';
|
|
std::exit(0);
|
|
}
|
|
return options;
|
|
}
|
|
|
|
// Currently not used, but depending on the applicationi you can choose to reply
|
|
// to incoming multicast packets.
|
|
Promise<void> sendSome(const Options &opt, AddressHandle dst,
|
|
size_t packets = 5, int interval = 1) {
|
|
Udp udp{opt.loop};
|
|
std::string message = "Hello back";
|
|
|
|
for (size_t i = 0; i < packets; i++) {
|
|
co_await udp.send(message, dst);
|
|
co_await sleep(opt.loop, 50 * interval);
|
|
}
|
|
udp.close();
|
|
}
|
|
|
|
Promise<void> printPackets(Options opt) {
|
|
Udp udp{opt.loop};
|
|
std::vector<Promise<void>> active;
|
|
|
|
try {
|
|
co_await udp.bind(opt.listenAddress, opt.port, UV_UDP_IPV6ONLY);
|
|
|
|
udp.joinMulticast(opt.multicastAddress, opt.listenAddress);
|
|
udp.setMulticastLoop(false);
|
|
|
|
fmt::print(stderr, "waiting for packets\n");
|
|
while (true) {
|
|
const auto [packet, from] = co_await udp.receiveOneFrom();
|
|
fmt::print("Received packet: {} from {}\n", packet, from.toString());
|
|
}
|
|
} catch (const UvcoException &e) {
|
|
fmt::print(stderr, "exception: {}\n", e.what());
|
|
}
|
|
udp.close();
|
|
}
|
|
|
|
int main(int argc, const char **argv) {
|
|
|
|
runMain<void>([&](const Loop &loop) {
|
|
Options opt = parseOptions(loop, argc, argv);
|
|
// Promise can be dropped! The coroutine still lives on the event loop.
|
|
return printPackets(opt);
|
|
});
|
|
}
|