#include <boost/asio.hpp>
#include <boost/asio/experimental/channel.hpp>
#include <iostream>
#include <memory>
using boost::asio::as_tuple;
using boost::asio::awaitable;
using boost::asio::dynamic_buffer;
using boost::asio::co_spawn;
using boost::asio::detached;
using boost::asio::experimental::channel;
using boost::asio::io_context;
using boost::asio::ip::tcp;
using boost::asio::steady_timer;
using namespace boost::asio::buffer_literals;
using namespace std::literals::chrono_literals;
class line_based_echo_session :
public std::enable_shared_from_this<line_based_echo_session>
{
tcp::socket socket_;
channel<void()> write_lock_{socket_.get_executor(), 1};
public:
line_based_echo_session(tcp::socket socket)
: socket_{std::move(socket)}
{
socket_.set_option(tcp::no_delay(true));
}
void start()
{
co_spawn(socket_.get_executor(),
[self = shared_from_this()]{ return self->handle_messages(); },
detached);
co_spawn(socket_.get_executor(),
[self = shared_from_this()]{ return self->send_heartbeats(); },
detached);
}
private:
void stop()
{
socket_.close();
write_lock_.cancel();
}
awaitable<void> handle_messages()
{
try
{
constexpr std::size_t max_line_length = 1024;
std::string data;
for (;;)
{
std::size_t length = co_await async_read_until(socket_,
dynamic_buffer(data, max_line_length), '\n');
co_await write_lock_.async_send();
co_await async_write(socket_, "<line>"_buf);
co_await async_write(socket_, dynamic_buffer(data, length));
write_lock_.try_receive([](auto...){});
}
}
catch (const std::exception&)
{
stop();
}
}
awaitable<void> send_heartbeats()
{
steady_timer timer{socket_.get_executor()};
try
{
for (;;)
{
timer.expires_after(1s);
co_await timer.async_wait();
co_await write_lock_.async_send();
co_await async_write(socket_, "<heartbeat>\n"_buf);
write_lock_.try_receive([](auto...){});
}
}
catch (const std::exception&)
{
stop();
}
}
};
awaitable<void> listen(tcp::acceptor& acceptor)
{
for (;;)
{
auto [e, socket] = co_await acceptor.async_accept(as_tuple);
if (!e)
{
std::make_shared<line_based_echo_session>(std::move(socket))->start();
}
}
}
int main(int argc, char* argv[])
{
try
{
if (argc != 3)
{
std::cerr << "Usage: mutual_exclusion_1";
std::cerr << " <listen_address> <listen_port>\n";
return 1;
}
io_context ctx;
auto listen_endpoint =
*tcp::resolver(ctx).resolve(argv[1], argv[2],
tcp::resolver::passive).begin();
tcp::acceptor acceptor(ctx, listen_endpoint);
co_spawn(ctx, listen(acceptor), detached);
ctx.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
}