2#ifndef _TIME_SHIELD_NTP_CLIENT_HPP_INCLUDED
3#define _TIME_SHIELD_NTP_CLIENT_HPP_INCLUDED
37 NtpClient(std::string server =
"pool.ntp.org",
int port = 123)
48 throw std::runtime_error(
"WSAStartup failed with error: " + std::to_string(
WsaGuard::instance().ret_code()));
51 SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
52 if (sock == INVALID_SOCKET) {
57 struct sockaddr_in addr{};
58 addr.sin_family = AF_INET;
59 addr.sin_port = htons(
static_cast<u_short
>(
m_port));
61 addrinfo hints{}, *res =
nullptr;
62 hints.ai_family = AF_INET;
63 hints.ai_socktype = SOCK_DGRAM;
64 hints.ai_protocol = IPPROTO_UDP;
66 if (getaddrinfo(
m_host.c_str(),
nullptr, &hints, &res) != 0 || !res) {
73 sockaddr_in* resolved =
reinterpret_cast<sockaddr_in*
>(res->ai_addr);
74 addr.sin_addr = resolved->sin_addr;
80 int timeout_ms = 5000;
81 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
reinterpret_cast<const char*
>(&timeout_ms),
sizeof(timeout_ms));
82 if (sendto(sock,
reinterpret_cast<const char*
>(&pkt),
sizeof(pkt), 0,
83 reinterpret_cast<sockaddr*
>(&addr),
sizeof(addr)) < 0) {
91 int from_len =
sizeof(from);
92 if (recvfrom(sock,
reinterpret_cast<char*
>(&pkt),
sizeof(pkt), 0,
93 reinterpret_cast<sockaddr*
>(&from), &from_len) < 0) {
102 int64_t result_offset;
183 std::memset(&pkt, 0,
sizeof(pkt));
188 const uint64_t frac = ((now_us % 1000000) * 0x100000000ULL) / 1000000;
190 pkt.
tx_ts_sec = htonl(
static_cast<uint32_t
>(sec));
191 pkt.
tx_ts_frac = htonl(
static_cast<uint32_t
>(frac));
199 (
static_cast<uint64_t
>(ntohl(pkt.
orig_ts_frac)) * 1000000 / 0xFFFFFFFFull);
201 (
static_cast<uint64_t
>(ntohl(pkt.
recv_ts_frac)) * 1000000 / 0xFFFFFFFFull);
203 (
static_cast<uint64_t
>(ntohl(pkt.
tx_ts_frac)) * 1000000 / 0xFFFFFFFFull);
206 result_offset_us = ((
static_cast<int64_t
>(receive_us) -
static_cast<int64_t
>(originate_us))
207 + (
static_cast<int64_t
>(transmit_us) -
static_cast<int64_t
>(arrival_us))) / 2;
218# warning "NtpClient is only supported on Windows for now."
223 static_assert(
sizeof(
void*) == 0,
"time_shield::NtpClient is only supported on Windows.");
Simple Windows-only NTP client for measuring time offset.
void fill_packet(ntp_packet &pkt) const
Converts local time to NTP timestamp format.
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.
bool parse_packet(const ntp_packet &pkt, int64_t &result_offset_us) const
Parses response and calculates offset.
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).
std::atomic< int64_t > m_offset_us
std::atomic< bool > m_is_success
bool query()
Queries the NTP server and updates the local offset.
static thread_local int s_last_error_code
static const WsaGuard & instance()
Returns the singleton instance, initializing WSA if needed.
int64_t now_realtime_us()
Get current real time in microseconds using a hybrid 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.