async_echo_server.cpp source
This is an echo server, implemented using callbacks. It is able to handle multiple connections in a single thread.
#include <array> #include <iostream> #include <memory> #include <string> #include <utility> #include <ark.hpp> #define PRINT_ACCESS_LOG namespace program { using namespace ark; namespace tcp = net::tcp; struct echo_service : public std::enable_shared_from_this<echo_service> { tcp::socket s_; std::array<char, 1024> buf_; #ifdef PRINT_ACCESS_LOG net::address addr_; echo_service(tcp::socket s, net::address addr) : s_(std::move(s)), addr_(std::move(addr)) { std::cout << "connected with peer " << peer_name() << std::endl; } ~echo_service() { std::cout << "closed connection to " << peer_name() << std::endl; } std::string peer_name() { auto ret = to_string(addr_); if (!ret) return std::string{"["} + ret.error().message() + "]"; return ret.value(); } #else echo_service(async_context &ctx, tcp::socket s) : s_(std::move(s)) {} #endif void do_echo() { async::read(s_, buffer(buf_), transfer_at_least(1), std::bind(&echo_service::handle_read, shared_from_this(), std::placeholders::_1)); } void handle_error(error_code ec) { std::cerr << ec.message() << std::endl; } void handle_write(result<size_t> ret) { if (ret.has_error()) { handle_error(ret.error()); return; } do_echo(); } void handle_read(result<size_t> ret) { if (ret.has_error()) { handle_error(ret.error()); return; } size_t sz = ret.value(); if (sz == 0) return; async::write(s_, buffer(buf_, sz), std::bind(&echo_service::handle_write, shared_from_this(), std::placeholders::_1)); } }; struct echo_server : public std::enable_shared_from_this<echo_server> { tcp::acceptor ac_; net::address addr_; echo_server(tcp::acceptor ac) : ac_(std::move(ac)) {} void run() { #ifdef PRINT_ACCESS_LOG tcp::async::accept(ac_, addr_, std::bind(&echo_server::handle_connection, shared_from_this(), std::placeholders::_1)); #else tcp::async::accept(ac_, std::bind(&echo_server::handle_connection, shared_from_this(), std::placeholders::_1)); #endif } void handle_connection(result<tcp::socket> ret) { if (!ret) { ac_.context().exit(ret.as_failure()); return; } #ifdef PRINT_ACCESS_LOG auto svc = std::make_shared<echo_service>(std::move(ret.value()), std::move(addr_)); #else auto svc = std::make_shared<echo_service>(std::move(ret.value())); #endif svc->do_echo(); run(); } }; result<void> run() { async_context ctx; TryX(ctx.init()); net::inet_address ep; TryX(ep.host("127.0.0.1")); ep.port(8080); auto ac = TryX(tcp::acceptor::create(ctx)); TryX(tcp::bind(ac, ep)); TryX(tcp::listen(ac)); auto srv = std::make_shared<echo_server>(std::move(ac)); srv->run(); TryX(ctx.run()); return success(); } } // namespace program int main(void) { auto ret = program::run(); if (ret.has_error()) { std::cerr << "error : " << ret.error().message() << std::endl; std::abort(); } return 0; }