C++ 获取网络时间并设置系统时间的实现方法

陆遥
陆遥
发布时间:2026-10-14| 5人浏览过
原创

本文详解如何在 C++ 中利用 Socket 通信从网络时间服务器获取标准时间,并将其转换为操作系统可识别的格式进行自动校准。

结果与目标

程序连接至指定 IP 地址的时间服务器,接收二进制时间数据,校准计算机的系统时钟。

准备条件

  1. 头文件引用:确保引入 windows.h 以使用 Socket API 及时间转换函数;引入 winsock2.h 处理网络相关类型。
  2. 权限要求:修改系统时间通常需要管理员权限,代码应在提权环境下运行。
  3. 依赖库:编译时需链接 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 以来的秒数。本地接收后需执行以下逻辑:

  1. 字节序转换:调用 ::ntohl() 将接收到的整型变量转为 CPU 本地字节序。
  2. 基准偏移计算: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++ 网络编程的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。