news 2026/4/20 4:45:57

基于c/c++实现linux/windows跨平台ntp时间戳服务器

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于c/c++实现linux/windows跨平台ntp时间戳服务器

目录

  • 使用场景
  • c/c++源码
  • 结果验证
    • windows编译命令
    • linux编译命令
    • 服务器输出结果
    • 客户端输出结果

使用场景

在某些严格要求时间同步很精准的项目中,获取网络ntp时间的时间延时比较大,做滤波处理可能效果也不理想。因此可以搭建一个本地ntp服务器,这样可以大大缩短网络链路,使得ntp时间更加精准。

c/c++源码

废话不多说,我们直接上源码,新建文件并命名为ntp_server.cpp,然后将下方源码拷贝到文件里。

#include<iostream>#include<cstdio>#include<cstring>#include<ctime>#include<stdint.h>// 跨平台套接字头文件与库#ifdef_WIN32#include<winsock2.h>#include<ws2tcpip.h>#pragmacomment(lib,"ws2_32.lib")typedefintsocklen_t;#definecloseclosesocket#else#include<unistd.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/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_DELTA=2208988800ULL;// 跨平台初始化网络库staticboolInitSocket(){#ifdef_WIN32WSADATA wsaData;if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0){std::cerr<<"Winsock initialization failed"<<std::endl;returnfalse;}#endifreturntrue;}// 跨平台清理网络库staticvoidCleanupSocket(){#ifdef_WIN32WSACleanup();#endif}// 获取当前系统时间转换为 NTP 时间戳staticvoidGetNtpTime(uint32_t&sec,uint32_t&frac){// 获取 Unix 时间 (1970-01-01 起的秒)time_t unixTime=time(nullptr);uint64_tntpSec=(uint64_t)unixTime+NTP_TIMESTAMP_DELTA;sec=htonl((uint32_t)ntpSec);// 小数部分: 简单填充 0 (生产环境可使用高精度时钟)frac=htonl(0);}intmain(){if(!InitSocket()){return-1;}// 创建 UDP 套接字SOCKET serverFd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);if(serverFd==INVALID_SOCKET){std::cerr<<"Failed to create socket"<<std::endl;CleanupSocket();return-1;}// 绑定 0.0.0.0:123 (NTP 默认端口)sockaddr_in serverAddr{};serverAddr.sin_family=AF_INET;serverAddr.sin_addr.s_addr=INADDR_ANY;serverAddr.sin_port=htons(123);if(bind(serverFd,(sockaddr*)&serverAddr,sizeof(serverAddr))<0){std::cerr<<"Failed to bind port 123! (Administrator/root privileges required)"<<std::endl;close(serverFd);CleanupSocket();return-1;}std::cout<<"NTP server started successfully, listening on 0.0.0.0:123"<<std::endl;std::cout<<"Waiting for client requests..."<<std::endl;// 循环接收请求并应答sockaddr_in clientAddr{};socklen_t clientLen=sizeof(clientAddr);NtpPacket packet{};while(true){memset(&packet,0,sizeof(packet));// 接收 NTP 请求ssize_t recvLen=recvfrom(serverFd,(char*)&packet,sizeof(packet),0,(sockaddr*)&clientAddr,&clientLen);if(recvLen<0)continue;// 打印客户端信息charclientIp[INET_ADDRSTRLEN];inet_ntop(AF_INET,&clientAddr.sin_addr,clientIp,INET_ADDRSTRLEN);std::cout<<"Received NTP request from "<<clientIp<<std::endl;// 构造 NTP 应答// LI=0, VN=3 (NTPv3), Mode=4 (Server)packet.li_vn_mode=(0<<6)|(3<<3)|4;packet.stratum=1;// 层级 1 (参考本地时钟)packet.poll=6;packet.precision=0xFA;// 填充时间戳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++-std=c++11 ntp_server.cpp-ontp_server-lws2_32

linux编译命令

g++-std=c++11 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
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/20 4:39:25

串口调试神器COMTransmit的隐藏功能:这样调试CH9143效率翻倍

串口调试神器COMTransmit的隐藏功能&#xff1a;这样调试CH9143效率翻倍 在嵌入式开发领域&#xff0c;串口调试就像工程师的"听诊器"&#xff0c;而COMTransmit无疑是这把听诊器的专业升级版。许多开发者仅仅把它当作基础收发工具&#xff0c;却不知道其中藏着能让你…

作者头像 李华
网站建设 2026/4/20 4:32:30

动态规划入门必刷:不同路径 最小路径和 详解

目录 一、不同路径&#xff08;中等难度&#xff09; 题目描述 核心思路分析 代码实现&#xff08;Java&#xff09; 复杂度分析 二、最小路径和&#xff08;中等难度&#xff09; 题目描述 核心思路分析 代码实现&#xff08;Java&#xff09; 复杂度分析 三、两道…

作者头像 李华
网站建设 2026/4/20 4:31:38

iOS开发避坑指南:IDFA、IDFV、UUID到底怎么选?别再混淆了!

iOS设备标识符深度解析&#xff1a;IDFA、IDFV与UUID的实战选择策略 每次在iOS项目中遇到设备标识需求时&#xff0c;面对IDFA、IDFV和UUID这三个选项&#xff0c;你是否也曾在深夜调试时对着文档陷入选择困难&#xff1f;作为经历过无数坑的老司机&#xff0c;我想分享一些实战…

作者头像 李华
网站建设 2026/4/20 4:29:29

Node-RED实战:从零构建轻量级MQTT Broker

1. 为什么选择Node-RED搭建MQTT Broker 最近在做一个智能家居项目&#xff0c;需要快速搭建一个本地的MQTT服务器来连接各种设备。原本考虑用Mosquitto这类专业方案&#xff0c;但发现配置起来太麻烦。后来发现Node-RED的aedes节点简直是个宝藏——5分钟就能搭好一个轻量级MQTT…

作者头像 李华