基于c/c++实现linux/windows跨平台ntp时间戳服务器
目录使用场景c/c源码结果验证windows编译命令linux编译命令服务器输出结果客户端输出结果使用场景在某些严格要求时间同步很精准的项目中获取网络ntp时间的时间延时比较大做滤波处理可能效果也不理想。因此可以搭建一个本地ntp服务器这样可以大大缩短网络链路使得ntp时间更加精准。c/c源码废话不多说我们直接上源码新建文件并命名为ntp_server.cpp然后将下方源码拷贝到文件里。#includeiostream#includecstdio#includecstring#includectime#includestdint.h// 跨平台套接字头文件与库#ifdef_WIN32#includewinsock2.h#includews2tcpip.h#pragmacomment(lib,ws2_32.lib)typedefintsocklen_t;#definecloseclosesocket#else#includeunistd.h#includesys/socket.h#includenetinet/in.h#includearpa/inet.h#defineSOCKETint#defineINVALID_SOCKET-1#endif// NTP 版本3 数据包结构 (RFC 1305)#pragmapack(push,1)structNtpPacket{// LI(2位) Version(3位) Mode(3位)uint8_tli_vn_mode;uint8_tstratum;// 层级uint8_tpoll;// 轮询间隔uint8_tprecision;// 精度uint32_trootDelay;// 根延迟uint32_trootDispersion;// 根离散度uint32_trefId;// 参考IDuint32_trefTimestampSec;// 参考时间戳(秒)uint32_trefTimestampFrac;// 参考时间戳(小数)uint32_torigTimestampSec;// 原始时间戳(秒)uint32_torigTimestampFrac;// 原始时间戳(小数)uint32_trecvTimestampSec;// 接收时间戳(秒)uint32_trecvTimestampFrac;// 接收时间戳(小数)uint32_ttransTimestampSec;// 发送时间戳(秒)uint32_ttransTimestampFrac;// 发送时间戳(小数)};#pragmapack(pop)// NTP 时间基准: 1900-01-01 到 1970-01-01 的秒数constuint64_tNTP_TIMESTAMP_DELTA2208988800ULL;// 跨平台初始化网络库staticboolInitSocket(){#ifdef_WIN32WSADATA wsaData;if(WSAStartup(MAKEWORD(2,2),wsaData)!0){std::cerrWinsock initialization failedstd::endl;returnfalse;}#endifreturntrue;}// 跨平台清理网络库staticvoidCleanupSocket(){#ifdef_WIN32WSACleanup();#endif}// 获取当前系统时间转换为 NTP 时间戳staticvoidGetNtpTime(uint32_tsec,uint32_tfrac){// 获取 Unix 时间 (1970-01-01 起的秒)time_t unixTimetime(nullptr);uint64_tntpSec(uint64_t)unixTimeNTP_TIMESTAMP_DELTA;sechtonl((uint32_t)ntpSec);// 小数部分: 简单填充 0 (生产环境可使用高精度时钟)frachtonl(0);}intmain(){if(!InitSocket()){return-1;}// 创建 UDP 套接字SOCKET serverFdsocket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);if(serverFdINVALID_SOCKET){std::cerrFailed to create socketstd::endl;CleanupSocket();return-1;}// 绑定 0.0.0.0:123 (NTP 默认端口)sockaddr_in serverAddr{};serverAddr.sin_familyAF_INET;serverAddr.sin_addr.s_addrINADDR_ANY;serverAddr.sin_porthtons(123);if(bind(serverFd,(sockaddr*)serverAddr,sizeof(serverAddr))0){std::cerrFailed to bind port 123! (Administrator/root privileges required)std::endl;close(serverFd);CleanupSocket();return-1;}std::coutNTP server started successfully, listening on 0.0.0.0:123std::endl;std::coutWaiting for client requests...std::endl;// 循环接收请求并应答sockaddr_in clientAddr{};socklen_t clientLensizeof(clientAddr);NtpPacket packet{};while(true){memset(packet,0,sizeof(packet));// 接收 NTP 请求ssize_t recvLenrecvfrom(serverFd,(char*)packet,sizeof(packet),0,(sockaddr*)clientAddr,clientLen);if(recvLen0)continue;// 打印客户端信息charclientIp[INET_ADDRSTRLEN];inet_ntop(AF_INET,clientAddr.sin_addr,clientIp,INET_ADDRSTRLEN);std::coutReceived NTP request from clientIpstd::endl;// 构造 NTP 应答// LI0, VN3 (NTPv3), Mode4 (Server)packet.li_vn_mode(06)|(33)|4;packet.stratum1;// 层级 1 (参考本地时钟)packet.poll6;packet.precision0xFA;// 填充时间戳GetNtpTime(packet.refTimestampSec,packet.refTimestampFrac);GetNtpTime(packet.recvTimestampSec,packet.recvTimestampFrac);GetNtpTime(packet.transTimestampSec,packet.transTimestampFrac);// 发送应答sendto(serverFd,(constchar*)packet,sizeof(packet),0,(sockaddr*)clientAddr,clientLen);}// 理论上不会执行到这里close(serverFd);CleanupSocket();return0;}结果验证windows编译命令g-stdc11 ntp_server.cpp-ontp_server-lws2_32linux编译命令g-stdc11 ntp_server.cpp-ontp_server服务器输出结果.\ntp_server.exe NTP server started successfully, listening on0.0.0.0:123 Waitingforclient requests... Received NTP request from192.168.0.149客户端输出结果客户端实现可参考基于c/c实现linux/windows跨平台获取ntp网络时间戳.\ntp_client.exe192.168.0.149 server address:192.168.0.149 Get Unix timestamp:1776611741000