From 7360e7eedecf13a1b006ead2c1e9d20d18fa6409 Mon Sep 17 00:00:00 2001 From: "Timur A. Fatkhullin" Date: Tue, 21 Jul 2026 17:55:45 +0300 Subject: [PATCH] add snplib_netproto.h --- include/snipplib/network/snplib_netproto.h | 64 ++++++++++++++++++++++ include/snipplib/network/snplib_network.h | 45 +++++++++++++-- include/snipplib/utils/snplib_string.h | 6 ++ 3 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 include/snipplib/network/snplib_netproto.h diff --git a/include/snipplib/network/snplib_netproto.h b/include/snipplib/network/snplib_netproto.h new file mode 100644 index 0000000..9e5d492 --- /dev/null +++ b/include/snipplib/network/snplib_netproto.h @@ -0,0 +1,64 @@ +#pragma once + +#include "../concepts/snplib_concepts.h" +#include "../utils/snplib_string.h" + +/* + + Various simple network session protocols + +*/ + +namespace snplib +{ + +static constexpr char SNPLIB_DEFAULT_NETPROTO_STOP_SEQ[] = "\n"; + +template +struct snplib_netproto_stopseq_t { + static constexpr std::string_view NETPROTO_STOP_SEQ{ + snplib_char_array_size(STOP_SEQ) ? STOP_SEQ : SNPLIB_DEFAULT_NETPROTO_STOP_SEQ}; + + // returns 2-element range of the input messsage and stop-sequence + template + requires snplib_char_range_c> // range of char ranges + OR toBytes(IR&& message) + { + OR res; + + std::back_inserter(res) = std::forward(message); + std::back_inserter(res) = std::remove_cvref_t{NETPROTO_STOP_SEQ.begin(), NETPROTO_STOP_SEQ.end()}; + + return res; + } + + // returns a copy of the input message + tralling stop-sequence + template + OR toBytes(IR&& message) + { + OR res; + + std::ranges::copy(std::forward(message), std::back_inserter(res)); + std::ranges::copy(NETPROTO_STOP_SEQ, std::back_inserter(res)); + + return res; + } + + template + auto toBytes(IR&& message) + { + if constexpr (std::ranges::contiguous_range) { + return std::array{std::span{message}, std::span{NETPROTO_STOP_SEQ}}; + } else { + return std::array{std::ranges::subrange{message}, std::ranges::subrange{NETPROTO_STOP_SEQ}}; + } + } + + template + OR fromBytes(IR&& bytes) + { + } +}; + + +} // namespace snplib \ No newline at end of file diff --git a/include/snipplib/network/snplib_network.h b/include/snipplib/network/snplib_network.h index 1be488f..5bbc27c 100644 --- a/include/snipplib/network/snplib_network.h +++ b/include/snipplib/network/snplib_network.h @@ -78,7 +78,7 @@ concept snplib_net_session_c = template -concept snplib_net_listener_c = requires(T t) { +concept snplib_net_listener_c = requires(T t, const T t_const) { // static constexpr member std::formattable; []() { [[maybe_unused]] static constexpr auto val = T::id; }(); @@ -90,6 +90,8 @@ concept snplib_net_listener_c = requires(T t) { { t.startListen() } -> snplib_error_c; t.stopListen(); + + { t_const.endpoint() } -> snplib_net_endpoint_c; }; @@ -111,12 +113,14 @@ enum class snplib_net_initiator_role_e : int { ROLE_SERVER, ROLE_CLIENT }; template concept snplib_net_initiator_c = - requires(T t) { + requires(T t, const T t_const) { // common part requires snplib_net_initiator_role_c; []() { [[maybe_unused]] static constexpr auto val = T::ROLE; }(); snplib_net_connection_c; + + { t_const.endpoint() } -> snplib_net_endpoint_c; } && ((T::ROLE != decltype(T::ROLE)::ROLE_SERVER) || requires(T t) { // use of short-circuiting logic!!! // server-role { t.accept() } -> snplib_async_retval_c; @@ -260,11 +264,43 @@ public: using LoggerT::logWarn; template - snplib_generic_netserver_t(std::unique_ptr listener) : _listener(std::move(listener)) + snplib_generic_netserver_t(std::tuple sess_manager_ctor_args, + std::tuple logger_ctor_args) + : SessManagerT(std::make_from_tuple(std::move(sess_manager_ctor_args))), + LoggerT(std::make_from_tuple(std::move(logger_ctor_args))) { } - virtual ~snplib_generic_netserver_t() = default; + virtual ~snplib_generic_netserver_t() + { + for (auto& wptr : _listeners) { + if (auto sptr = wptr.lock()) { + sptr->stopListen(); + } + } + }; + + size_t addListener(std::unique_ptr listener) {} + + auto startServer(std::unique_ptr listener) + { + auto err = listener->startListen(); + if (err) { + co_return std::unexpect(snplib_deduced_error(err, std::make_error_code(std::errc::io_error))); + } + + auto listener_sptr = std::make_shared(std::move(listener)); + _listeners.emplace_back(listener_sptr); + + while (true) { + auto connection = co_await listener_sptr->accept(); + if (connection) { + auto sptr = std::make_shared(); + _sessions.insert(sptr); + sptr->start(std::move(connection.value())); + } + } + } auto startServer() { @@ -292,6 +328,7 @@ public: protected: std::unique_ptr _listener{}; + std::vector> _listeners{}; std::set> _sessions{}; }; diff --git a/include/snipplib/utils/snplib_string.h b/include/snipplib/utils/snplib_string.h index c90ffe7..75db433 100644 --- a/include/snipplib/utils/snplib_string.h +++ b/include/snipplib/utils/snplib_string.h @@ -9,6 +9,12 @@ namespace snplib { +// compile-time size of zero-terminated char array +static consteval size_t snplib_char_array_size(const char* arr) +{ + return *arr ? 1 + snplib_char_array_size(arr + 1) : 0; +} + constexpr static bool snplib_is_space(char in) noexcept { static constexpr std::array ws = {' ', '\t', '\n', '\v', '\r', '\f'};