Boost.Asio is a C++ library for network and low-level I/O programming.
One of its official examples shows the use of multicast to transmit packets to a group of subscribers. This official multicast example includes two programs: receiver.cpp and sender.cpp .
The following Multicast Messenger program is inspired by the official multicast example. It works as a transceiver which integrates the functionalities of transmitting and receiving packets. Given a multicast address, this messenger program provides a local chat room without relying a specific server.
#include <iostream>
#include <sstream>
#include <string>
#include <array>
#include <boost/asio.hpp>
constexpr short multicast_port = 30001;
class messenger
{
public:
messenger(boost::asio::io_context& io_context,
const boost::asio::ip::address& listen_address,
const boost::asio::ip::address& multicast_address);
~messenger();
void do_send(std::string);
private:
void do_transmit();
void do_receive();
private:
boost::asio::ip::udp::endpoint transmit_endpoint_;
boost::asio::ip::udp::endpoint receive_endpoint_;
boost::asio::ip::udp::socket transmit_socket_;
boost::asio::ip::udp::socket receive_socket_;
std::string nickname_;
std::string message_; // the message to send
std::array<char, 1024> data_; // the data received
};
int main(int argc, char* argv[])
{
try
{
// All programs that use asio need to have at least one I/O execution context.
// An I/O execution context provides access to I/O functionality.
boost::asio::io_context io_context;
// "0.0.0.0" means listen on every available network interface in this contex
std::string listen_address_raw = "0.0.0.0";
// The multicast addresses range from 239.0.0.0 to 239.255.255.255 are the administratively scoped addresses.
// They would be considered local, not globally unique,
// and can be reused in domains administered by different organizations.
std::string multicast_address_raw = "239.255.0.1";
// Construct a new messenger
messenger s(io_context,
boost::asio::ip::make_address(listen_address_raw),
boost::asio::ip::make_address(multicast_address_raw));
// Use thread t for io_context
std::thread t([&io_context]() { io_context.run(); });
// Get and send messages
std::string line;
while (std::getline(std::cin, line))
{
s.do_send(line);
}
// Terminate thread t
t.join();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
messenger::messenger(boost::asio::io_context& io_context,
const boost::asio::ip::address& listen_address,
const boost::asio::ip::address& multicast_address)
: transmit_endpoint_(multicast_address, multicast_port),
receive_endpoint_(listen_address, multicast_port),
transmit_socket_(io_context, transmit_endpoint_.protocol()),
receive_socket_(io_context),
nickname_("noname")
{
// Set a nickname
std::cout << "Welcome! Please input your nickname: ";
std::getline(std::cin, nickname_);
std::cout << "Hello " << nickname_ << "! You can chat now!" << std::endl;
// Create the socket so that multiple may be bound to the same address.
receive_socket_.open(receive_endpoint_.protocol());
receive_socket_.set_option(boost::asio::ip::udp::socket::reuse_address(true));
receive_socket_.bind(receive_endpoint_);
// Join the multicast group.
receive_socket_.set_option(
boost::asio::ip::multicast::join_group(multicast_address));
// Listen to receive data
do_receive();
}
messenger::~messenger()
{
std::cout << "Destructor of the messenger class." << std::endl;
}
void messenger::do_send(std::string line)
{
message_ = line;
do_transmit();
}
void messenger::do_transmit()
{
std::ostringstream os;
os << nickname_ << ": " << message_;
message_ = os.str();
transmit_socket_.async_send_to(
boost::asio::buffer(message_), transmit_endpoint_,
[this](boost::system::error_code ec, std::size_t /*length*/)
{
// pass
});
}
void messenger::do_receive()
{
receive_socket_.async_receive_from(
boost::asio::buffer(data_), receive_endpoint_,
[this](boost::system::error_code ec, std::size_t length)
{
if (!ec)
{
std::cout.write(data_.data(), length);
std::cout << std::endl;
do_receive();
}
});
}