8 #ifndef XCLOX_SIMPLE_QUERY_HPP
9 #define XCLOX_SIMPLE_QUERY_HPP
13 #define ASIO_NO_DEPRECATED
25 template <
typename T,
int N>
26 struct DefaultTimeout {
27 static const int ms = N;
29 template <
typename T,
int N>
30 const int DefaultTimeout<T, N>::ms;
48 using Callback = std::function<void(
const asio::ip::udp::endpoint&,
const asio::error_code&,
const Packet&,
const std::chrono::steady_clock::duration&)>;
54 explicit QuerySingle(asio::io_context& io,
const std::chrono::milliseconds& timeout)
55 : m_timer(io, timeout)
56 , m_socket(io, asio::ip::udp::v4())
68 static std::weak_ptr<QuerySingle>
start(asio::io_context& io,
const asio::ip::udp::endpoint& server,
Callback callback,
const std::chrono::milliseconds& timeout = std::chrono::milliseconds(DefaultTimeout::ms))
73 auto query = std::make_shared<QuerySingle>(io, timeout);
74 query->m_timer.async_wait([query](
const asio::error_code& error) {
75 if (error != asio::error::operation_aborted) {
76 query->m_timer.expires_at(std::chrono::steady_clock::time_point::min());
80 Packet packet(0, 4, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Timestamp(std::chrono::system_clock::now()).value());
81 const auto& time = std::chrono::steady_clock::now();
82 query->m_socket.async_send_to(
83 asio::buffer(packet.
data()),
85 [query, server, callback, packet, time](
const asio::error_code& error, std::size_t) {
87 query->m_timer.cancel();
88 callback(server, error, packet, std::chrono::steady_clock::now() - time);
91 query->m_socket.async_receive_from(
92 asio::buffer(query->m_buffer),
94 [query, server, callback, time](
const asio::error_code& error, std::size_t size) {
95 query->m_timer.cancel();
96 if (error || size != query->m_buffer.size() || query->m_timer.expiry() == std::chrono::steady_clock::time_point::max()) {
98 query->m_timer.expiry() == std::chrono::steady_clock::time_point::max() ? asio::error::operation_aborted : (query->m_timer.expiry() == std::chrono::steady_clock::time_point::min() ? asio::error::timed_out : (error ? error : asio::error::message_size)),
100 std::chrono::steady_clock::now() - time);
102 callback(server, error, Packet(query->m_buffer), std::chrono::steady_clock::now() - time);
112 m_timer.expires_at(std::chrono::steady_clock::time_point::max());
119 if (m_socket.is_open()) {
125 asio::steady_timer m_timer;
126 asio::ip::udp::socket m_socket;
127 asio::ip::udp::endpoint m_endpoint;
128 Packet::DataType m_buffer;
Packet is an immutable raw NTP packet.
Definition: packet.hpp:54
DataType data() const
Returns a raw data representation of the underlying packet.
Definition: packet.hpp:113
QuerySingle is an ephemeral class representing a single NTP query.
Definition: query_single.hpp:41
static std::weak_ptr< QuerySingle > start(asio::io_context &io, const asio::ip::udp::endpoint &server, Callback callback, const std::chrono::milliseconds &timeout=std::chrono::milliseconds(DefaultTimeout::ms))
Starts querying the given endpoint.
Definition: query_single.hpp:68
QuerySingle(asio::io_context &io, const std::chrono::milliseconds &timeout)
Constructs a single NTP query on the given context that runs within the given timeout duration.
Definition: query_single.hpp:54
internal::DefaultTimeout< QuerySingle, 3000 > DefaultTimeout
Type of query timeout milliseconds holder.
Definition: query_single.hpp:49
std::function< void(const asio::ip::udp::endpoint &, const asio::error_code &, const Packet &, const std::chrono::steady_clock::duration &)> Callback
Type of query callback.
Definition: query_single.hpp:48
void cancel()
Cancels the query reporting asio::error::operation_aborted to the caller.
Definition: query_single.hpp:110
Timestamp is an immutable class representing a NTP timestamp.
Definition: timestamp.hpp:45