diff --git a/common/adc_traits.h b/common/adc_traits.h index 1f39843..3d76ed1 100644 --- a/common/adc_traits.h +++ b/common/adc_traits.h @@ -1,11 +1,11 @@ #pragma once +#include #include #include #include #include - /* ABSTRACT DEVICE COMPONENTS LIBRARY @@ -202,4 +202,12 @@ template concept adc_tuple_like = adc_is_tuple_v == true; +template +// from https://stackoverflow.com/questions/74383254/concept-that-models-only-the-stdchrono-duration-types +concept adc_time_duration_c = requires { + [](std::type_identity>) {}(std::type_identity()); +}; + + + } // namespace adc::traits diff --git a/net/adc_net_concepts.h b/net/adc_net_concepts.h index b085713..8153471 100644 --- a/net/adc_net_concepts.h +++ b/net/adc_net_concepts.h @@ -6,7 +6,6 @@ ABSTRACT DEVICE COMPONENTS LIBRARY */ -#include #include #include "../common/adc_traits.h" @@ -18,28 +17,21 @@ namespace adc::interfaces { -template -// from https://stackoverflow.com/questions/74383254/concept-that-models-only-the-stdchrono-duration-types -concept adc_time_duration_c = requires { - [](std::type_identity>) {}(std::type_identity()); -}; - - -template +template struct adc_duration_common_type; -template +template struct adc_duration_common_type : std::common_type { }; -template +template struct adc_duration_common_type : adc_duration_common_type, Ts...> { }; -template +template using adc_duration_common_type_t = typename adc_duration_common_type::type; @@ -92,7 +84,7 @@ template -concept adc_netservice_c = traits::adc_input_char_range && traits::adc_output_char_range && adc_time_duration_c && +concept adc_netservice_c = traits::adc_input_char_range && traits::adc_output_char_range && traits::adc_time_duration_c && requires(SRVT srv, const SRVT srv_const) { typename SRVT::netservice_ident_t; diff --git a/net/adc_netservice_asio.h b/net/adc_netservice_asio.h index db24e1d..1af2480 100644 --- a/net/adc_netservice_asio.h +++ b/net/adc_netservice_asio.h @@ -40,6 +40,8 @@ #include "adc_netmsg.h" +#include "adc_net_concepts.h" + namespace adc::traits { @@ -70,6 +72,192 @@ concept adc_asio_inet_stream_proto_c = requires(T t, asio_streambuff_iter_t begi namespace adc::impl { +template +concept adc_asio_transport_proto_c = + std::derived_from || std::derived_from || + std::derived_from || std::derived_from; + + +template +concept adc_asio_stream_transport_proto_c = + std::derived_from || std::derived_from; + + + +template SESSION_PROTOT> +class AdcNetServiceASIOBase : public TRANSPORT_PROTOT, public SESSION_PROTOT +{ +public: + typedef std::string netservice_ident_t; + + using socket_t = typename TRANSPORT_PROTOT::socket; + using endpoint_t = typename TRANSPORT_PROTOT::endpoint; + + struct asio_async_ctx_t { + bool use_future = false; + std::function accept_comp_token; + std::function connect_comp_token; + std::function send_comp_token; + + template + static std::unordered_map> + receive_comp_token; + }; + + static constexpr std::chrono::duration DEFAULT_ACCEPT_TIMEOUT = std::chrono::years::max(); + static constexpr std::chrono::duration DEFAULT_CONNECT_TIMEOUT = std::chrono::seconds(10); + static constexpr std::chrono::duration DEFAULT_SEND_TIMEOUT = std::chrono::seconds(5); + static constexpr std::chrono::duration DEFAULT_RECEIVE_TIMEOUT = std::chrono::seconds(5); + + + netservice_ident_t ident() const + { + return _ident; + } + + + template + auto asyncConnect(const endpoint_t& endpoint, + asio_async_ctx_t& ctx, + const TimeoutT& timeout = DEFAULT_CONNECT_TIMEOUT) + { + auto timer = getDeadlineTimer(timeout); + + if (ctx.use_future) { + return _socket.async_connect( + endpoint, asio::use_future([&ctx, timer = std::move(timer)](std::error_code ec) { timer->cancel(); })); + } else { + return _socket.async_connect(endpoint, [&ctx, timer = std::move(timer)](std::error_code ec) { + timer->cancel(); + ctx.connect_comp_token(ec); + }); + } + } + + + template + auto asyncAccept(const endpoint_t& endpoint, + asio_async_ctx_t& ctx, + const TimeoutT& timeout = DEFAULT_ACCEPT_TIMEOUT) + { + if constexpr (std::derived_from>) { + return; // there is no acceptor for UDP protocol + } + + typename TRANSPORT_PROTOT::acceptor acceptor; + try { + acceptor = typename TRANSPORT_PROTOT::acceptor(_ioContext, endpoint); + } catch (std::system_error err) { + ctx.accept_comp_token(err.code()); + return; + } + + auto timer = getDeadlineTimer(timeout); + + if (ctx.use_future) { + return _socket.async_accept( + endpoint, asio::use_future([&ctx, timer = std::move(timer)](std::error_code ec) { timer->cancel(); })); + } else { + return _socket.async_accept(endpoint, [&ctx, timer = std::move(timer)](std::error_code ec) { + timer->cancel(); + ctx.accept_comp_token(ec); + }); + } + } + + template + auto asyncReceive(asio_async_ctx_t& ctx, const TimeoutT& timeout = DEFAULT_RECEIVE_TIMEOUT) + { + auto timer = getDeadlineTimer(timeout); + + auto s_res = std::make_shared>(); + + if constexpr (std::derived_from>) { + return asio::async_read_until( + _socket, _streamBuffer, + [s_res, this](IT begin, IT end) { + *s_res = this->search(std::span(begin, end)); + return std::make_tuple(std::get<1>(*s_res), std::get<2>(*s_res)); + }, + [&ctx, s_res, timer = std::move(timer), this](std::error_code ec, size_t) { + timer->cancel(); + if (ec) { + return; + } + + R msg; + std::string_view net_pack{std::get<0>(*s_res), std::get<1>(*s_res)}; + + std::ranges::copy(this->fromProto(net_pack), std::back_inserter(msg)); + _streamBuffer.consume(net_pack.size()); + + ctx.accept_comp_token(ec, std::move(msg)); + }); + } + } + + + template + auto accept(const endpoint_t& endpoint, const TimeoutT& timeout) + { + asio_async_ctx_t ctx = {.use_future = true}; + std::future ftr = asyncAcept(endpoint, ctx, timeout); + ftr.get(); + } + + template + auto connect(const endpoint_t& endpoint, const TimeoutT& timeout) + { + asio_async_ctx_t ctx = {.use_future = true}; + std::future ftr = asyncConnect(endpoint, ctx, timeout); + ftr.get(); + } + + template + auto send(const R& msg, const TimeoutT& timeout) + { + std::future ftr = asyncSend(msg, timeout, asio::use_future); + ftr.get(); + } + + template + auto receive(const TimeoutT& timeout) + { + std::future ftr = asyncReceive(timeout, asio::use_future); + return ftr.get(); + } + +protected: + netservice_ident_t _ident; + + asio::io_context& _ioContext; + + socket_t _socket; + + // acceptor_t _acceptor; + + asio::streambuf _streamBuffer; + + template + std::unique_ptr getDeadlineTimer(const TimeoutT& timeout, bool arm = true) + { + std::unique_ptr timer(_socket.get_executor()); + + if (arm) { + timer->expires_after(timeout); + + timer->async_wait([this](const std::error_code& ec) { + if (!ec) { + _socket.cancel(std::make_error_code(std::errc::timed_out)); + } + }); + } + + return timer; + } +}; + template class AdcNetServiceASIO : public InetProtoT