12-Daytime.7-A combined TCP and UDP asynchronous server

12-Daytime.7-A combined TCP and UDP asynchronous server

#include <array>
#include <ctime>
#include <functional>
#include <memory>

#include <asio.hpp>
#include <spdlog/spdlog.h>

using asio::ip::tcp;
using asio::ip::udp;

std::string make_daytime_string()
{
    using namespace std;
    time_t now = time(nullptr);
    return ctime(&now);
}

class tcp_connection
    : public std::enable_shared_from_this<tcp_connection> {
public:
    using pointer = std::shared_ptr<tcp_connection>;

    static pointer create(asio::io_context& io)
    {
        return pointer(new tcp_connection(io));
    }

    tcp::socket& socket()
    {
        return socket_;
    }

    void start()
    {
        auto message = make_daytime_string();

        asio::async_write(socket_, asio::buffer(message),
            std::bind(&tcp_connection::handle_write, shared_from_this()));
    }

private:
    tcp_connection(asio::io_context& io)
        : socket_(io)
    {
        spdlog::info("tcp_connection created at {}", make_daytime_string());
    }

    void handle_write() { }

private:
    tcp::socket socket_;
    std::string message_;
};

class tcp_server {
public:
    tcp_server(asio::io_context& io_context)
        : io_context_(io_context)
        , acceptor_(io_context, tcp::endpoint(tcp::v4(), 13))
    {
        start_accept();
    }

private:
    void start_accept()
    {
        tcp_connection::pointer new_connection = tcp_connection::create(io_context_);

        acceptor_.async_accept(new_connection->socket(),
            std::bind(&tcp_server::handle_accept,
                this, new_connection, asio::placeholders::error));
    }

    void handle_accept(tcp_connection::pointer new_connection,
        const std::error_code& error)
    {
        if (!error) {
            new_connection->start();
        }
        start_accept();
    }

private:
    asio::io_context& io_context_;
    tcp::acceptor acceptor_;
};

class udp_server {
public:
    udp_server(asio::io_context& io_context)
        : socket_(io_context, udp::endpoint(udp::v4(), 13))
    {
        start_receive();
    }

private:
    void start_receive()
    {
        socket_.async_receive_from(
            asio::buffer(recv_buffer_),
            remote_endpoint_,
            std::bind(&udp_server::handle_receive,
                this, asio::placeholders::error));
    }

    void handle_receive(const std::error_code& error)
    {
        if (!error) {
            std::shared_ptr<std::string> message(new std::string(make_daytime_string()));

            socket_.async_send_to(
                asio::buffer(*message),
                remote_endpoint_,
                std::bind(&udp_server::handle_send, this, message));

            start_receive();
        }
    }

    void handle_send(std::shared_ptr<std::string> message)
    {
    }

private:
    udp::socket socket_;
    udp::endpoint remote_endpoint_;
    std::array<char, 1> recv_buffer_;
};

int main()
{
    try {
        asio::io_context io;
        tcp_server tcp_server_(io);
        udp_server udp_server_(io);
        io.run();
    } catch (const std::exception& e) {
        spdlog::error("Exception: {}", e.what());
    }
}