Created
December 11, 2024 12:51
-
-
Save shardator/a66acd5262f94d0b58002c4990ac9eaa to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <boost/asio.hpp> | |
#include <iostream> | |
#include <string> | |
#include <stdexcept> | |
#include <unordered_map> | |
class MulticastServer { | |
public: | |
// Constructor throws on critical errors (e.g., invalid bind address or socket open fail). | |
explicit MulticastServer(const std::string &bind_address = "0.0.0.0", int failure_threshold = 3) | |
: io_context_(), | |
socket_(io_context_, boost::asio::ip::udp::v4()), | |
next_group_id_(1), | |
failure_threshold_(failure_threshold) | |
{ | |
boost::system::error_code ec; | |
boost::asio::ip::udp::endpoint local_endpoint( | |
boost::asio::ip::address::from_string(bind_address, ec), 0); | |
if (ec) { | |
throw std::runtime_error("Invalid bind address: " + bind_address + " Error: " + ec.message()); | |
} | |
socket_.open(local_endpoint.protocol(), ec); | |
if (ec) { | |
throw std::runtime_error("Failed to open socket: " + ec.message()); | |
} | |
socket_.bind(local_endpoint, ec); | |
if (ec) { | |
throw std::runtime_error("Failed to bind socket: " + ec.message()); | |
} | |
} | |
// Registers a multicast group and returns an integer group_id. | |
// Throws if address is invalid or not multicast. | |
int register_group(const std::string &group_address, unsigned short port) { | |
boost::system::error_code ec; | |
auto address = boost::asio::ip::address::from_string(group_address, ec); | |
if (ec) { | |
throw std::runtime_error("Invalid multicast address: " + group_address + ", error: " + ec.message()); | |
} | |
if (!address.is_multicast()) { | |
throw std::runtime_error("Address " + group_address + " is not a multicast address."); | |
} | |
boost::asio::ip::udp::endpoint endpoint(address, port); | |
int group_id = next_group_id_++; | |
GroupInfo ginfo{endpoint, 0}; | |
group_map_[group_id] = ginfo; | |
return group_id; | |
} | |
// Sends a raw bytes message to the previously registered multicast group (by ID). | |
// Returns true on success, false on a single send failure. | |
// If a group fails consecutively more than 'failure_threshold_' times, throws an exception. | |
bool send_to_group(int group_id, const std::string &message) { | |
auto it = group_map_.find(group_id); | |
if (it == group_map_.end()) { | |
throw std::out_of_range("Group ID " + std::to_string(group_id) + " not registered."); | |
} | |
auto &ginfo = it->second; | |
boost::system::error_code ec; | |
std::size_t bytes_sent = socket_.send_to(boost::asio::buffer(message), ginfo.endpoint, 0, ec); | |
if (ec) { | |
// Increment failure count | |
ginfo.consecutive_failures++; | |
// Check if we've reached the threshold | |
if (ginfo.consecutive_failures > failure_threshold_) { | |
throw std::runtime_error( | |
"Group " + std::to_string(group_id) + " exceeded failure threshold with error: " + ec.message()); | |
} | |
// Return false for a single failure | |
return false; | |
} | |
// On success, reset failures to 0 | |
ginfo.consecutive_failures = 0; | |
return true; | |
} | |
// If you need asynchronous operations, you could run the io_context here. | |
void run() { | |
io_context_.run(); | |
} | |
private: | |
struct GroupInfo { | |
boost::asio::ip::udp::endpoint endpoint; | |
int consecutive_failures; | |
}; | |
boost::asio::io_context io_context_; | |
boost::asio::ip::udp::socket socket_; | |
std::unordered_map<int, GroupInfo> group_map_; | |
int next_group_id_; | |
int failure_threshold_; | |
}; | |
// Example usage: | |
// | |
// int main() { | |
// try { | |
// MulticastServer server; | |
// int group_id = server.register_group("239.255.0.1", 30001); | |
// // Try sending a message | |
// if (!server.send_to_group(group_id, "Hello Multicast Group 1!")) { | |
// std::cerr << "Send attempt failed once.\n"; | |
// } | |
// } catch (const std::exception &ex) { | |
// std::cerr << "Error: " << ex.what() << "\n"; | |
// } | |
// return 0; | |
// } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment