6#include <boost/asio/as_tuple.hpp>
7#include <boost/asio/awaitable.hpp>
8#include <boost/asio/experimental/awaitable_operators.hpp>
9#include <boost/asio/system_timer.hpp>
10#include <boost/asio/this_coro.hpp>
11#include <boost/asio/use_awaitable.hpp>
12#include <boost/asio/use_future.hpp>
13#include <boost/system/detail/error_code.hpp>
17#include <fmt/ranges.h>
22#include <spdlog/spdlog.h>
31 template <
typename SocketType>
33 requires(SocketType socket, std::shared_ptr<typename SocketType::ConnectionType>
connection) {
34 requires std::derived_from<SocketType, SpecialSocket>;
35 requires not std::is_same_v<SocketType, SpecialSocket>;
37 asio::buffer(socket.get_response_msg_buffer());
41 { socket.response_handler(
UDPEndpoint{}, std::span<char>{}) } -> std::same_as<void>;
42 { socket.is_finished() } -> std::same_as<bool>;
43 { socket.print_error() } -> std::same_as<void>;
44 { socket.register_send_action_imp(asio::awaitable<void>{},
connection) } -> std::same_as<void>;
47 class SpecialSocket :
public std::enable_shared_from_this<SpecialSocket>
52 -> std::expected<std::shared_ptr<SocketType>, boost::system::error_code>;
56 asio::awaitable<void> action,
57 const std::shared_ptr<
typename std::remove_cvref_t<
decltype(self)>::ConnectionType>&
connection);
65 std::chrono::seconds waiting_time = std::chrono::seconds(2)) -> std::future<void>;
83 std::shared_future<std::variant<std::monostate, std::monostate>>
listen_future_;
92 template <SpecialSocketDerived SocketType>
98 -> std::expected<std::shared_ptr<SocketType>, boost::system::error_code>
101 std::shared_ptr<SocketType>(
new SocketType{ port_number, io_context, std::forward<Args>(args)... });
102 auto error_code = socket->get_socket_error_code();
105 spdlog::critical(
"Local socket failed to bind to the port {} due to the error: {}",
107 error_code.message());
108 return std::unexpected{ error_code };
110 socket->listen(io_context);
114 template <SpecialSocketDerived SocketType>
117 spdlog::debug(
"Local socket with port {} starts to listen ...", socket->get_port());
118 socket->bind_socket();
119 auto remote_endpoint = udp::endpoint{};
122 const auto [err_code, receive_size] =
co_await socket->get_socket().async_receive_from(
123 asio::buffer(socket->get_response_msg_buffer()), remote_endpoint, asio::as_tuple(asio ::use_awaitable));
126 auto read_msg = std::span{ socket->get_response_msg_buffer().data(), receive_size };
127 [[maybe_unused]]
auto remote_ec = boost::system::error_code{};
128 socket->response_handler(remote_endpoint, read_msg);
132 spdlog::trace(
"Coroutine for the local socket with port {} has existed.", socket->get_port());
138 asio::awaitable<void> action,
139 const std::shared_ptr<
typename std::remove_cvref_t<
decltype(self)>::ConnectionType>&
connection)
141 spdlog::trace(
"Registering send action from connection {} with remote endpoint {}.",
144 self.register_send_action_imp(std::move(action),
connection);
145 spdlog::trace(
"Send action to the remote endpoint {} has been registered.",
152 using boost::asio::experimental::awaitable_operators::operator||;
153 self.listen_future_ =
154 asio::co_spawn(io_context,
162 std::chrono::seconds waiting_time) -> std::future<void>
164 auto waiting_action = [](
auto socket, std::chrono::seconds waiting_time) -> asio::awaitable<void>
166 spdlog::trace(
"Waiting local socket with port {} to finish listening ...", socket->get_port());
167 if (socket->is_finished())
169 spdlog::trace(
"Local socket with port {} finished listening.", socket->get_port());
172 auto timer = asio::system_timer{
co_await asio::this_coro::executor };
173 timer.expires_after(waiting_time);
174 [[maybe_unused]]
auto err_code =
co_await timer.async_wait(asio::as_tuple(asio::use_awaitable));
175 socket->cancel_timer_.cancel();
176 if (not socket->is_finished())
178 socket->print_error();
181 return asio::co_spawn(
auto get_socket() const -> auto &
auto cancel_listen_after(this auto &&self, io_context_type &io_context, std::chrono::seconds waiting_time=std::chrono::seconds(2)) -> std::future< void >
asio::system_timer cancel_timer_
Used for cancel the unfinished coroutine.
auto is_valid() const -> bool
auto cancel_coroutine() -> asio::awaitable< void >
auto get_cancel_timer() -> auto &
static auto create(int port_number, io_context_type &io_context, Args... args) -> std::expected< std::shared_ptr< SocketType >, boost::system::error_code >
auto wait_for_listen_finish(std::chrono::seconds time) -> std::optional< std::future_status >
auto get_socket_error_code() const -> auto
std::unique_ptr< udp::socket > socket_
boost::system::error_code socket_ec_
auto get_future() -> auto &
void listen(this auto &self, io_context_type &io_context)
SpecialSocket(int port_number, io_context_type &io_context)
void register_send_action(this auto &&self, asio::awaitable< void > action, const std::shared_ptr< typename std::remove_cvref_t< decltype(self)>::ConnectionType > &connection)
std::shared_future< std::variant< std::monostate, std::monostate > > listen_future_
static auto listen_all_connections(std::shared_ptr< SocketType > socket) -> asio::awaitable< void >
constexpr auto get_shared_from_this(auto &&obj)
boost::asio::ip::basic_endpoint< boost::asio::ip::udp > UDPEndpoint
asio::thread_pool io_context_type