Ubuntu C++ NTP校时程序

📅 发布时间:2026/8/25 14:04:57
Ubuntu C++ NTP校时程序
需要sudo执行#include iostream #include cstring #include ctime #include sys/socket.h #include netinet/in.h #include netdb.h #include unistd.h #include sys/time.h #include arpa/inet.h #include endian.h #include iomanip struct NTPPacket { uint8_t li_vn_mode; uint8_t stratum; uint8_t poll; uint8_t precision; uint32_t rootDelay; uint32_t rootDispersion; uint32_t refId; uint64_t refTs; uint64_t originTs; uint64_t recvTs; uint64_t transTs; }; // 正确的 NTP 时间戳转换处理字节序 time_t ntp_to_unix(uint64_t ntp_ts) { // 关键将网络字节序转换为主机字节序 uint64_t host_ts be64toh(ntp_ts); // 提取秒部分高32位 uint32_t seconds host_ts 32; // 减去 NTP epoch (1900) 到 Unix epoch (1970) 的秒数 return (time_t)(seconds - 2208988800UL); } // 打印字节序转换后的值 void print_packet_info(const NTPPacket packet, bool is_big_endian) { std::cout \n NTP Packet Details std::endl; // 1. 第一个字节的解析 std::cout LI-VN-Mode: 0x std::hex std::setw(2) std::setfill(0) (int)packet.li_vn_mode std::dec std::setfill( ); uint8_t mode packet.li_vn_mode 0x07; uint8_t version (packet.li_vn_mode 3) 0x07; uint8_t leap (packet.li_vn_mode 6) 0x03; std::cout (Leap (int)leap , Version (int)version , Mode (int)mode ); if (mode 4) std::cout [Server Response]; else if (mode 3) std::cout [Client Request]; std::cout std::endl; // 2. Stratum层级 std::cout Stratum: (int)packet.stratum; if (packet.stratum 0) std::cout (unspecified); else if (packet.stratum 1) std::cout (primary reference); else std::cout (secondary reference); std::cout std::endl; // 3. Poll轮询间隔 std::cout Poll: (int)packet.poll (2^ (int)packet.poll (1 packet.poll) seconds) std::endl; // 4. Precision精度 std::cout Precision: (int)packet.precision (2^ (int)packet.precision (1.0 / (1 (-packet.precision))) seconds) std::endl; // 5. Root Delay根延迟 uint32_t rootDelay ntohl(packet.rootDelay); std::cout Root Delay: 0x std::hex std::setw(8) std::setfill(0) packet.rootDelay std::dec std::setfill( ); std::cout ( (rootDelay / 65536.0) seconds) std::endl; // 6. Root Dispersion根离散度 uint32_t rootDisp ntohl(packet.rootDispersion); std::cout Root Dispersion: 0x std::hex std::setw(8) std::setfill(0) packet.rootDispersion std::dec std::setfill( ); std::cout ( (rootDisp / 65536.0) seconds) std::endl; // 7. Reference ID参考ID std::cout Reference ID: 0x std::hex std::setw(8) std::setfill(0) ntohl(packet.refId) std::dec std::setfill( ); // 尝试作为IP地址解析 uint32_t refId ntohl(packet.refId); if (refId 0x01000000 refId 0xFE000000) { struct in_addr addr; addr.s_addr packet.refId; std::cout (IP: inet_ntoa(addr) ); } else { // 可能是4个字符的ID char ref[5]; memcpy(ref, packet.refId, 4); ref[4] \0; std::cout (Code: ref ); } std::cout std::endl; // 8. 时间戳重点 auto print_timestamp [](const char* name, uint64_t ts) { // 原始值 std::cout name (raw): 0x std::hex std::setw(16) std::setfill(0) ts std::dec std::setfill( ); // 转换为正确的时间 uint64_t host_ts be64toh(ts); uint32_t seconds host_ts 32; uint32_t fraction host_ts 0xFFFFFFFF; if (seconds 0) { time_t t seconds - 2208988800UL; std::cout ctime(t); std::cout Fraction: 0x std::hex std::setw(8) std::setfill(0) fraction std::dec std::setfill( ); std::cout ( (fraction / 4294967296.0 * 1000000) microseconds) std::endl; } else { std::cout (zero) std::endl; } }; std::cout \n--- Timestamps --- std::endl; print_timestamp(Reference Timestamp, packet.refTs); print_timestamp(Origin Timestamp, packet.originTs); print_timestamp(Receive Timestamp, packet.recvTs); print_timestamp(Transmit Timestamp, packet.transTs); // 9. 计算出网络延迟和偏差如果有Origin和Receive时间 if (packet.originTs ! 0 packet.recvTs ! 0) { uint64_t origin_host be64toh(packet.originTs); uint64_t recv_host be64toh(packet.recvTs); uint64_t trans_host be64toh(packet.transTs); uint32_t origin_sec origin_host 32; uint32_t recv_sec recv_host 32; uint32_t trans_sec trans_host 32; if (origin_sec 0 recv_sec 0 trans_sec 0) { time_t t1 origin_sec - 2208988800UL; time_t t2 recv_sec - 2208988800UL; time_t t3 trans_sec - 2208988800UL; double origin_f (origin_host 0xFFFFFFFF) / 4294967296.0; double recv_f (recv_host 0xFFFFFFFF) / 4294967296.0; double trans_f (trans_host 0xFFFFFFFF) / 4294967296.0; double origin_time origin_sec origin_f; double recv_time recv_sec recv_f; double trans_time trans_sec trans_f; // 简单计算假设客户端发送时间和接收时间对称 std::cout \n--- Network Statistics --- std::endl; std::cout Client send time: std::fixed std::setprecision(3) origin_time std::endl; std::cout Server receive time: recv_time std::endl; std::cout Server transmit time: trans_time std::endl; // 往返延迟 (当前时间 - 发送时间) - (服务器处理时间) // 这里简化计算 time_t now; time(now); double rtt (now 0.0) - origin_time; std::cout Estimated RTT: rtt seconds std::endl; } } std::cout std::endl; } // 打印原始字节数据十六进制 void print_raw_data(const uint8_t* data, size_t len) { std::cout \n Raw Packet Data (Hex) std::endl; for (size_t i 0; i len; i) { std::cout std::hex std::setw(2) std::setfill(0) (int)data[i] ; } std::cout std::endl; std::cout std::endl; } // 改进版获取时间并正确处理字节序 //bool get_time_via_ntp(const char* server, time_t outTime) { // int sockfd socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); // if (sockfd 0) return false; // struct sockaddr_in serverAddr; // struct hostent *serverHost gethostbyname(server); // if (serverHost nullptr) { // close(sockfd); // return false; // } // memset(serverAddr, 0, sizeof(serverAddr)); // serverAddr.sin_family AF_INET; // serverAddr.sin_port htons(123); // memcpy(serverAddr.sin_addr, serverHost-h_addr_list[0], serverHost-h_length); // // 设置超时 // struct timeval tv; // tv.tv_sec 5; // tv.tv_usec 0; // setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, tv, sizeof(tv)); // NTPPacket packet; // memset(packet, 0, sizeof(packet)); // packet.li_vn_mode 0x1B; // NTP 请求 // if (sendto(sockfd, packet, sizeof(packet), 0, // (struct sockaddr*)serverAddr, sizeof(serverAddr)) 0) { // close(sockfd); // return false; // } // socklen_t addrLen sizeof(serverAddr); // if (recvfrom(sockfd, packet, sizeof(packet), 0, // (struct sockaddr*)serverAddr, addrLen) 0) { // close(sockfd); // return false; // } // close(sockfd); // // 调试输出打印原始时间戳可选 // std::cout Raw timestamp: 0x std::hex packet.transTs std::dec std::endl; // // 打印原始数据 // print_raw_data((const uint8_t*)packet, addrLen); // // 打印解析后的字段 // print_packet_info(packet, true); // outTime ntp_to_unix(packet.transTs); // // 验证时间合理性应该在 2020-2030 之间 // if (outTime 1577836800 || outTime 1893456000) { // 2020-2030 // std::cout Warning: suspicious time: ctime(outTime); // return false; // } // return true; //} // 改进版获取时间并正确处理字节序使用 getaddrinfo bool get_time_via_ntp(const char* server, time_t outTime) { int sockfd socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sockfd 0) return false; // 设置超时 struct timeval tv; tv.tv_sec 5; tv.tv_usec 0; setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, tv, sizeof(tv)); // 使用 getaddrinfo 替代 gethostbyname struct addrinfo hints, *results, *rp; memset(hints, 0, sizeof(hints)); hints.ai_family AF_INET; // 只使用 IPv4 hints.ai_socktype SOCK_DGRAM; // UDP hints.ai_protocol IPPROTO_UDP; int status getaddrinfo(server, 123, hints, results); if (status ! 0) { std::cerr getaddrinfo error: gai_strerror(status) std::endl; close(sockfd); return false; } // 尝试所有返回的地址 bool success false; struct sockaddr_in serverAddr; memset(serverAddr, 0, sizeof(serverAddr)); for (rp results; rp ! NULL; rp rp-ai_next) { if (rp-ai_family AF_INET rp-ai_socktype SOCK_DGRAM) { memcpy(serverAddr, rp-ai_addr, sizeof(struct sockaddr_in)); // getaddrinfo 已经设置了端口但确保它是 123 serverAddr.sin_port htons(123); success true; break; } } freeaddrinfo(results); if (!success) { std::cerr No valid IPv4 address found for: server std::endl; close(sockfd); return false; } // 构造 NTP 请求包 NTPPacket packet; memset(packet, 0, sizeof(packet)); packet.li_vn_mode 0x1B; // LI0, VN3, Mode3 (Client Request) if (sendto(sockfd, packet, sizeof(packet), 0, (struct sockaddr*)serverAddr, sizeof(serverAddr)) 0) { perror(sendto failed); close(sockfd); return false; } socklen_t addrLen sizeof(serverAddr); if (recvfrom(sockfd, packet, sizeof(packet), 0, (struct sockaddr*)serverAddr, addrLen) 0) { perror(recvfrom failed (timeout or no response)); close(sockfd); return false; } close(sockfd); // 调试输出打印原始时间戳可选 std::cout Raw timestamp: 0x std::hex packet.transTs std::dec std::endl; // 打印原始数据 print_raw_data((const uint8_t*)packet, addrLen); // 打印解析后的字段 print_packet_info(packet, true); outTime ntp_to_unix(packet.transTs); // 验证时间合理性应该在 2020-2030 之间 if (outTime 1577836800 || outTime 1893456000) { // 2020-2030 std::cout Warning: suspicious time: ctime(outTime); return false; } return true; } // 备用方案使用多个服务器并返回成功 bool get_time_with_fallback(time_t outTime) { const char* servers[] { ntp.aliyun.com, ntp1.aliyun.com, ntp.tencent.com, pool.ntp.org, cn.pool.ntp.org, time.google.com }; for (int i 0; i 6; i) { std::cout Testing: servers[i] ... ; if (get_time_via_ntp(servers[i], outTime)) { std::cout SUCCESS std::endl; return true; } std::cout FAILED std::endl; } return false; } int main() { time_t syncTime 0; std::cout NTP Time Sync (Fixed Endian) std::endl; if (!get_time_with_fallback(syncTime)) { std::cerr \nAll NTP servers failed. std::endl; return 1; } // 验证时间 struct tm* tm_info localtime(syncTime); if (tm_info-tm_year 120 || tm_info-tm_year 130) { // 2020-2030 std::cerr Warning: Time seems incorrect: ctime(syncTime); std::cerr Please check your systems timezone settings. std::endl; } // 设置系统时间 struct timeval tv; tv.tv_sec syncTime; tv.tv_usec 0; if (settimeofday(tv, nullptr) 0) { perror(Failed to set system time (need sudo)); return 1; } std::cout \n✓ System time updated to: ctime(syncTime); // 显示当前时间 time_t now; time(now); std::cout Current system time: ctime(now); return 0; }