C++ 获取网络时间并设置系统时间的实现方法
本文详解如何在 C++ 中利用 Socket 通信从网络时间服务器获取标准时间,并将其转换为操作系统可识别的格式进行自动校准。
结果与目标
程序连接至指定 IP 地址的时间服务器,接收二进制时间数据,校准计算机的系统时钟。
准备条件
- 头文件引用:确保引入
windows.h以使用 Socket API 及时间转换函数;引入winsock2.h处理网络相关类型。 - 权限要求:修改系统时间通常需要管理员权限,代码应在提权环境下运行。
- 依赖库:编译时需链接
ws2_32.lib。
真实操作顺序
第1步:初始化网络环境
启动 Socket 服务。此处假设已存在一个类似 CInitSock 的初始化类或宏(对应 WinSock 的 WSAStartup),用于建立基础的 Socket 通信能力。
第2步:创建并配置 Socket
创建一个面向流的 TCP 套接字,并指定连接目标服务器的参数:
- 协议族:
AF_INET(IPv4)。 - 类型:
SOCK_STREAM(TCP)。 - 传输层协议:
IPPROTO_TCP。 - 目标端口:
37(这是标准的 Time Protocol 端口)。
第3步:建立连接与接收数据
发起对远程主机的连接。推荐使用稳定的公共时间源 IP(例如 69.25.96.13),避免使用可能屏蔽该端口的国内特定 IP(如原注提到的 210.72.145.44)。连接成功后,接收 sizeof(ULONG) 长度的字节流。
第4步:数据转换与解析
服务器发送的是网络字节序(大端)的自 1900-01-01 以来的秒数。本地接收后需执行以下逻辑:
- 字节序转换:调用
::ntohl()将接收到的整型变量转为 CPU 本地字节序。 - 基准偏移计算:Windows 的
SYSTEMTIME是基于 1601 年的,而传统时间协议常用 1900 年作为起始点。需计算出 1900 年到 1601 年的秒差,或通过FILETIME中间转换。
第5步:应用系统时间
将处理后的时间填充至 SYSTEMTIME 结构体,随后调用 Windows API SetSystemTime(&st) 生效。
完成判断与源码参考
当控制台无报错输出且系统托盘的时间显示正确更新时,即代表同步成功。以下是完整的核心代码实现片段:
// 根据返回的原始时间戳设置系统时间
void setTimeFromTP(ULONG ulTime)
{
FILETIME ft;
SYSTEMTIME st;
// 设置基准时间 1900-01-01 00:00:00
st.wYear = 1900;
st.wMonth = 1;
st.wDay = 1;
st.wHour = 0;
st.wMinute = 0;
st.wSecond = 0;
st.wMilliseconds = 0;
// 将基准时间转换成 windows 文件时间
SystemTimeToFileTime(&st, &ft);
LONGLONG* pLLong = (LONGLONG*)&ft;
// 注意:这里简单增加了时间差值,实际严谨做法需扣除 1900-1601 年的时间差或依据具体协议定义
// 假设 ulTime 是距离 1970 或 1900 的秒数,此处依示例保留直接增加逻辑
*pLLong += (LONGLONG)10000000 * ulTime;
FileTimeToSystemTime(&ft, &st);
::SetSystemTime(&st);
}
void main()
{
CInitSock initSock; // 初始化 WinSock
SOCKET s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s == INVALID_SOCKET)
{
printf("socket error...");
return;
}
sockaddr_in servAddr = {0};
servAddr.sin_family = AF_INET;
servAddr.sin_port = ::htons(37);
// 建议使用稳定外网 IP,避免国内某些节点阻断
servAddr.sin_addr.S_un.S_addr = ::inet_addr("69.25.96.13");
if (SOCKET_ERROR == ::connect(s, (sockaddr*)&servAddr, sizeof(servAddr)))
{
printf("connect error..");
return;
}
// 接收时间 (32位 ULONG)
ULONG ulTime = 0;
int nRecv = ::recv(s, (char*)&ulTime, sizeof(ulTime), 0);
if (nRecv > 0)
{
ulTime = ::ntohl(ulTime); // 转换为本地主机序
setTimeFromTP(ulTime);
}
else
{
printf("recv error...");
return;
}
::closesocket(s);
printf("*******************************");
getchar();
}
原文有依据的排错
- 连接失败或超时:如果
connect报错,请检查防火墙是否拦截了出站 TCP 37 端口。原草稿提到部分国内 IP(如210.72.145.44)可能会不返回值或被运营商封锁,建议切换为其他公共 NTP/Time 服务器地址。 - 时间偏差过大:如果同步后时间错误,检查代码中对时间基准年份的处理。
FILETIME起始于 1601 年 1 月 1 日,而常见的 UNIX/Linux 时间戳起始于 1970 年,传统 TIME 协议多基于 1900 年。必须确保代码中的时间累加或偏移量与协议定义完全匹配,否则会出现数百年的偏差。 - 权限不足:在 Windows Vista/7/10/11 等高版本系统中,普通用户无权修改系统时间。需右键以“管理员身份运行”程序。
简要总结
以上关于 C++ 设置系统时间及系统时间网络更新的详细内容,更多关于 C++ 网络编程的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









