3#ifndef _TIME_SHIELD_NTP_CLIENT_HPP_INCLUDED
4#define _TIME_SHIELD_NTP_CLIENT_HPP_INCLUDED
17#if TIME_SHIELD_ENABLE_NTP_CLIENT
30#if TIME_SHIELD_PLATFORM_WINDOWS
41 NtpClient(std::string server =
"pool.ntp.org",
int port = 123)
52 throw std::runtime_error(
"WSAStartup failed with error: " + std::to_string(
WsaGuard::instance().ret_code()));
55 SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
56 if (sock == INVALID_SOCKET) {
61 struct sockaddr_in addr{};
62 addr.sin_family = AF_INET;
63 addr.sin_port = htons(
static_cast<u_short
>(
m_port));
65 addrinfo hints{}, *res =
nullptr;
66 hints.ai_family = AF_INET;
67 hints.ai_socktype = SOCK_DGRAM;
68 hints.ai_protocol = IPPROTO_UDP;
70 if (getaddrinfo(
m_host.c_str(),
nullptr, &hints, &res) != 0 || !res) {
77 sockaddr_in* resolved =
reinterpret_cast<sockaddr_in*
>(res->ai_addr);
78 addr.sin_addr = resolved->sin_addr;
84 int timeout_ms = 5000;
85 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
reinterpret_cast<const char*
>(&timeout_ms),
sizeof(timeout_ms));
86 if (sendto(sock,
reinterpret_cast<const char*
>(&pkt),
sizeof(pkt), 0,
87 reinterpret_cast<sockaddr*
>(&addr),
sizeof(addr)) < 0) {
95 int from_len =
sizeof(from);
96 if (recvfrom(sock,
reinterpret_cast<char*
>(&pkt),
sizeof(pkt), 0,
97 reinterpret_cast<sockaddr*
>(&from), &from_len) < 0) {
106 int64_t result_offset;
184 static thread_local int value = 0;
190 if (v < 0)
return false;
191 out =
static_cast<uint64_t
>(v);
197 const uint64_t frac =
static_cast<uint64_t
>(ntohl(frac_net));
198 return (frac * 1000000ULL) >> 32;
203 if (sec < 0)
return false;
204 out_us =
static_cast<uint64_t
>(sec) * 1000000ULL +
ntp_frac_to_us(frac_net);
210 std::memset(&pkt, 0,
sizeof(pkt));
221 const uint64_t frac = ((now_us % 1000000) * 0x100000000ULL) / 1000000;
223 pkt.
tx_ts_sec = htonl(
static_cast<uint32_t
>(sec));
224 pkt.
tx_ts_frac = htonl(
static_cast<uint32_t
>(frac));
229 uint64_t arrival_us = 0;
232 uint64_t originate_us = 0, receive_us = 0, transmit_us = 0;
239 const int64_t t1 =
static_cast<int64_t
>(originate_us);
240 const int64_t t2 =
static_cast<int64_t
>(receive_us);
241 const int64_t t3 =
static_cast<int64_t
>(transmit_us);
242 const int64_t t4 =
static_cast<int64_t
>(arrival_us);
244 result_offset_us = ((t2 - t1) + (t3 - t4)) / 2;
252#elif TIME_SHIELD_PLATFORM_UNIX
254#include <arpa/inet.h>
256#include <netinet/in.h>
257#include <sys/socket.h>
268 NtpClient(std::string server =
"pool.ntp.org",
int port = 123)
278 const int sock = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
285 addrinfo hints{}, *res =
nullptr;
286 hints.ai_family = AF_INET;
287 hints.ai_socktype = SOCK_DGRAM;
288 hints.ai_protocol = IPPROTO_UDP;
290 const int resolve_code = getaddrinfo(
m_host.c_str(),
nullptr, &hints, &res);
291 if (resolve_code != 0 || !res) {
299 addr.sin_family = AF_INET;
300 addr.sin_port = htons(
static_cast<uint16_t
>(
m_port));
301 addr.sin_addr =
reinterpret_cast<sockaddr_in*
>(res->ai_addr)->sin_addr;
310 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout,
sizeof(timeout));
312 const ssize_t sent = ::sendto(sock, &pkt,
sizeof(pkt), 0,
reinterpret_cast<sockaddr*
>(&addr),
sizeof(addr));
321 socklen_t from_len =
sizeof(from);
322 const ssize_t received = ::recvfrom(sock, &pkt,
sizeof(pkt), 0,
reinterpret_cast<sockaddr*
>(&from), &from_len);
332 int64_t result_offset = 0;
357 uint64_t system_time_us = 0;
359 return static_cast<int64_t
>(system_time_us) +
m_offset_us.load();
411 static thread_local int value = 0;
417 if (v < 0)
return false;
418 out =
static_cast<uint64_t
>(v);
424 const uint64_t frac =
static_cast<uint64_t
>(ntohl(frac_net));
425 return (frac * 1000000ULL) >> 32;
430 if (sec < 0)
return false;
431 out_us =
static_cast<uint64_t
>(sec) * 1000000ULL +
ntp_frac_to_us(frac_net);
437 std::memset(&pkt, 0,
sizeof(pkt));
438 pkt.li_vn_mode = (0 << 6) | (3 << 3) | 3;
448 const uint64_t frac = ((now_us % 1000000) * 0x100000000ULL) / 1000000;
450 pkt.tx_ts_sec = htonl(
static_cast<uint32_t
>(sec));
451 pkt.tx_ts_frac = htonl(
static_cast<uint32_t
>(frac));
456 uint64_t arrival_us = 0;
459 uint64_t originate_us = 0, receive_us = 0, transmit_us = 0;
461 if (!
ntp_ts_to_unix_us(pkt.orig_ts_sec, pkt.orig_ts_frac, originate_us))
return false;
462 if (!
ntp_ts_to_unix_us(pkt.recv_ts_sec, pkt.recv_ts_frac, receive_us))
return false;
463 if (!
ntp_ts_to_unix_us(pkt.tx_ts_sec, pkt.tx_ts_frac, transmit_us))
return false;
466 const int64_t t1 =
static_cast<int64_t
>(originate_us);
467 const int64_t t2 =
static_cast<int64_t
>(receive_us);
468 const int64_t t3 =
static_cast<int64_t
>(transmit_us);
469 const int64_t t4 =
static_cast<int64_t
>(arrival_us);
471 result_offset_us = ((t2 - t1) + (t3 - t4)) / 2;
481# warning "NtpClient is unsupported on this platform."
489 static_assert(
sizeof(
void*) == 0,
"time_shield::NtpClient is unsupported on this platform.");
499# warning "NtpClient is disabled or unsupported on this platform."
507 static_assert(
sizeof(
void*) == 0,
"time_shield::NtpClient is disabled by configuration.");
Windows implementation of the NTP client for measuring time offset.
void fill_packet(ntp_packet &pkt) const
Converts local time to NTP timestamp format.
static bool get_now_us_u64(uint64_t &out) noexcept
bool success() const noexcept
Returns whether the last NTP query was successful.
NtpClient(std::string server="pool.ntp.org", int port=123)
Constructs NTP client with specified host and port.
static uint64_t ntp_frac_to_us(uint32_t frac_net) noexcept
bool parse_packet(const ntp_packet &pkt, int64_t &result_offset_us) const
Parses response and calculates offset.
std::atomic< bool > m_is_success
int64_t get_utc_time_us() const noexcept
Returns current UTC time in microseconds based on last NTP offset.
static constexpr int64_t NTP_TIMESTAMP_DELTA
Seconds between 1900 and 1970 epochs.
time_t get_utc_time() const noexcept
Returns current UTC time as time_t (seconds since Unix epoch).
int64_t get_offset_us() const noexcept
Returns the last measured offset in microseconds.
int64_t get_utc_time_ms() const noexcept
Returns current UTC time in milliseconds based on last NTP offset.
int get_last_error_code() const noexcept
Returns last WinSock error code (if any).
static bool ntp_ts_to_unix_us(uint32_t sec_net, uint32_t frac_net, uint64_t &out_us) noexcept
std::atomic< int64_t > m_offset_us
static int & last_error_code_slot() noexcept
bool query()
Queries the NTP server and updates the local offset.
static const WsaGuard & instance()
Returns the singleton instance, initializing WSA if needed.
Configuration macros for the library.
int64_t now_realtime_us()
Get current real time in microseconds using a platform-specific method.
Main namespace for the Time Shield library.
Структура пакета NTP Total: 384 bits or 48 bytes.
Header file with time-related utility functions.
Singleton guard for WinSock initialization.