基于c/c++实现linux/windows跨平台获取ntp网络时间戳

张开发
2026/4/18 14:51:16 15 分钟阅读

分享文章

基于c/c++实现linux/windows跨平台获取ntp网络时间戳
目录使用场景c/c源码结果验证windows编译命令linux编译命令输出结果使用场景在项目中有时需要根据时钟同步做一些操作例如网络请求、多设备视频同步播放等。在多设备同步播放视频的环境中我们可以定时请求网络时间并根据每次请求的耗时取均值做一下滤波这样就能使得我们的各设备同步播放视频更加准确。c/c源码废话不多说我们直接上源码新建文件并命名为ntp_time.cpp然后将下方源码拷贝到文件里。#includecstdint#includecstring#includestring#includeiostream#ifdef_WIN32#includewinsock2.h#includews2tcpip.h#includewindows.h#else#includesys/socket.h#includenetinet/in.h#includearpa/inet.h#includeunistd.h#includenetdb.h#endif// NTP 客户端返回码#defineNTP_OK0#defineNTP_ERR_WSA_INIT-1#defineNTP_ERR_SOCKET-2#defineNTP_ERR_INVALID_IP-3#defineNTP_ERR_SENDTO-4#defineNTP_ERR_RECV-5#defineNTP_ERR_DNS-6/** * brief 设置请求超时 * param sock 套接字fd * param timeout_ms 超时时间单位毫秒 */staticvoidSetSocketTimeout(intsock,inttimeout_ms){#ifdef_WIN32DWORD timeout(DWORD)timeout_ms;setsockopt(sock,SOL_SOCKET,SO_RCVTIMEO,(constchar*)timeout,sizeof(timeout));#elsestructtimevaltv{};tv.tv_sectimeout_ms/1000;tv.tv_usec(timeout_ms%1000)*1000;setsockopt(sock,SOL_SOCKET,SO_RCVTIMEO,tv,sizeof(tv));#endif}/** * brief 关闭套接字 * param sock 套接字fd */staticvoidCloseSocket(intsock){#ifdef_WIN32closesocket(sock);#elseclose(sock);#endif}// NTP 报文结构体 (SNTPv4 RFC 4330)structNtpPacket{uint8_tli_vn_mode0x1B;// 00 110 011: 无警告、NTPv4、客户端模式uint8_tstratum0;// 服务器层级uint8_tpoll0;// 轮询间隔uint8_tprecision0;// 时钟精度uint32_troot_delay0;// 根延迟uint32_troot_dispersion0;// 误差范围uint32_tref_id0;// 参考IDuint32_tref_ts_sec0;// 参考时间戳(秒)uint32_tref_ts_frac0;// 参考时间戳(小数秒)uint32_torig_ts_sec0;// 客户端发起请求时间(秒)uint32_torig_ts_frac0;// 客户端发起请求时间(小数秒)uint32_trecv_ts_sec0;// 服务器接收请求时间(秒)uint32_trecv_ts_frac0;// 服务器接收请求时间(小数秒)uint32_ttrans_ts_sec0;// 服务器回复时间(秒)【核心字段】uint32_ttrans_ts_frac0;// 服务器回复时间(小数秒)};/** * brief 获取 NTP 时间戳(毫秒级) * param ntp_time_ms 出参毫秒级ntp时间 * param server ntp服务器地址 * param timeout_ms 请求超时时间单位毫秒 * return NTP_OK 获取成功其他 获取失败 */staticintGetNtpTimestamp(int64_tntp_time_ms,conststd::stringserverntp.aliyun.com,inttimeout_ms2000){ntp_time_ms0;#ifdef_WIN32WSADATA wsaData;if(WSAStartup(MAKEWORD(2,2),wsaData)!0){returnNTP_ERR_WSA_INIT;}#endifintsocksocket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);if(sock0){#ifdef_WIN32WSACleanup();#endifreturnNTP_ERR_SOCKET;}SetSocketTimeout(sock,timeout_ms);// 域名解析structaddrinfohints{};hints.ai_familyAF_INET;// IPv4 onlyhints.ai_socktypeSOCK_DGRAM;hints.ai_protocolIPPROTO_UDP;structaddrinfo*resultnullptr;intretgetaddrinfo(server.c_str(),123,hints,result);if(ret!0||resultnullptr){CloseSocket(sock);#ifdef_WIN32WSACleanup();#endifreturnNTP_ERR_DNS;}// 直接使用解析后的地址structsockaddr_inaddr{};memcpy(addr,result-ai_addr,result-ai_addrlen);freeaddrinfo(result);// 必须释放// NTP 报文 (SNTPv4)structNtpPacketpkt{};if(sendto(sock,(constchar*)pkt,sizeof(pkt),0,(structsockaddr*)addr,sizeof(addr))0){CloseSocket(sock);#ifdef_WIN32WSACleanup();#endifreturnNTP_ERR_SENDTO;}socklen_t addr_lensizeof(addr);if(recvfrom(sock,(char*)pkt,sizeof(pkt),0,(structsockaddr*)addr,addr_len)0){CloseSocket(sock);#ifdef_WIN32WSACleanup();#endifreturnNTP_ERR_RECV;}// // 关键秒 小数 → 毫秒级时间戳// uint64_tsecntohl(pkt.trans_ts_sec);uint64_tfracntohl(pkt.trans_ts_frac);constint64_tNTP_OFFSET2208988800LL;int64_tunix_sec(int64_t)sec-NTP_OFFSET;int64_tms(int64_t)((double)frac*1000.0/4294967296.0);ntp_time_msunix_sec*1000LLms;CloseSocket(sock);#ifdef_WIN32WSACleanup();#endifreturnNTP_OK;}intmain(intargc,char*argv[]){std::string ntp_servive{ntp.aliyun.com};if(2argc){ntp_serviveargv[1];}std::coutserver address:ntp_servivestd::endl;int64_ttime_now0;intretGetNtpTimestamp(time_now,ntp_servive,2000);if(retNTP_OK){std::coutGet Unix timestamp:time_nowstd::endl;}else{std::coutFailed to get timestamp,error code:retstd::endl;}return0;}结果验证windows编译命令g-stdc11 ntp_time.cpp-o ntp_time-lws2_32linux编译命令g-stdc11 ntp_time.cpp-o ntp_time输出结果$ ./ntp_time.exe Get Unix timestamp:1776244593733

更多文章