_临时子程序 () 返回 (0
) time =
GetNTPTime () 输出调试文本 (强制类型转换 (time,
#日期时间型 ))
计次循环首 (5,
)
time =
NTP时间 (NTP服务器池 (), 500
)
如果真 (time ≠ 0
)
跳出循环 ()

计次循环尾 ()返回 (time
)变量名 | 类 型 | 静态 | 数组 | 备 注 |
TimeURL | 文本型 | | 0 |
n | 整数型 | | |
TimeURL =
分割文本 ( #NTP_SERVER, #换行符, )
变量循环首 (取数组成员数 (TimeURL
), 1, -1, n
)
如果真 (TimeURL
[n
] =
“”)
删除成员 (TimeURL, n,
)

变量循环尾 ()置随机数种子 (取启动时间 ())
n =
取随机数 (1,
取数组成员数 (TimeURL
))
输出调试文本 (“NTP:” + TimeURL
[n
])返回 (TimeURL
[n
])|
NTP时间 | 长整数型 | |
|
NTP_URL | 文本型 | | | |
timeout | 整数型 | | | |
变量名 | 类 型 | 静态 | 数组 | 备 注 |
NTP_PORT | 整数型 | | |
数据包 | 字节集 | | |
hSocket | | | |
hConnect | | | |
iResult | | | |
补偿值 | | | |
时标 | 长整数型 | | 4 |
时间数据 | 字节集 | | |
正确时间 | 日期时间型 | | |
NULL | 长整数型 | | |
T | 字节集 | | 3 |
Buffer | 整数型 | | |
ret | 整数型 | | |
NTP_PORT = 123
如果真 (套接字_加载服务 ())

hSocket =
套接字_创建 ()
如果真 (hSocket ≠ 0
)

如果真 (套接字_置超时 (hSocket, timeout
))



hConnect =
套接字_连接 (hSocket, NTP_URL, NTP_PORT
)


如果真 (hConnect ≠ -1
)



时标
[1
] =
取时间标识 ()



数据包 =
{ 27
} +
取重复字节集 (39,
{ 0
}) +
到字节集 (时标
[1
]) 



如果真 (套接字_发送 (hSocket, 数据包
))





时标
[4
] =
取时间标识 ()




Buffer =
_申请内存 (48
)




ret =
Recv (hSocket, Buffer, 48, 0
)




如果真 (ret ≠ -1
)





时间数据 =
指针到字节集 (Buffer, 48
)





T
[1
] =
取字节集中间 (时间数据, 25, 8
)





T
[2
] =
取字节集中间 (时间数据, 33, 4
)





T
[3
] =
取字节集中间 (时间数据, 41, 4
)





时标
[2
] =
转换字节序 (T
[2
])





时标
[3
] =
转换字节序 (T
[3
])





补偿值
= (时标
[2
] - 时标
[1
] + 时标
[3
] - 时标
[4
]) ÷ 2






如果真 (T
[1
] =
到字节集 (时标
[1
]))







正确时间 =
增减时间 (取现行时间 (),
#秒, 补偿值
)





_释放内存 (Buffer
)





套接字_销毁 (hConnect
)





套接字_销毁 (hSocket
)





套接字_卸载服务 ()





返回 (取字节集数据 (到字节集 (正确时间
),
#长整数型, ))

















_释放内存 (Buffer
)










套接字_销毁 (hConnect
)








套接字_销毁 (hSocket
)

套接字_卸载服务 ()返回 (NULL)返回 (到数值 (时标字节组
[1
]) × 16777216 +
到数值 (时标字节组
[2
]) × 65536 +
到数值 (时标字节组
[3
]) × 256 +
到数值 (时标字节组
[4
]))
时标 =
取时间间隔 (取现行时间 (),
[1900年1月1日
],
#秒 ) + 4294967296
返回 (时标 - 28800
) 变量名 | 类 型 | 静态 | 数组 | 备 注 |
lpHost | 整数型 | | |
HOST | HOSTENT | | |
dwIPAddr | 整数型 | | |
tmpIPAddr | 字节型 | | 0 |
i | 整数型 | | |
IP地址 | 文本型 | | |
lpHost =
GetHostByName (到文本 (域名
))
如果真 (lpHost = 0
)
WSACleanup ()
返回 (0
)RtlMoveMemoryAny (HOST, lpHost, 16)RtlMoveMemoryGetInt (dwIPAddr, HOST.hAddrList, 4
)重定义数组 (tmpIPAddr, 假, HOST.hLen
)RtlMoveMemoryBit (tmpIPAddr, dwIPAddr, HOST.hLen
)
计次循环首 (HOST.hLen, i
)
如果 (i = HOST.hLen
)

IP地址 = IP地址 +
到文本 (tmpIPAddr
[i
])

IP地址 = IP地址 +
到文本 (tmpIPAddr
[i
]) +
“.”

计次循环尾 ()返回 (Inet_Addr (IP地址
))
置入代码 ({ 139, 69, 8
})
判断 (类型 =
#字节型 )
置入代码 ({ 185, 1, 1, 0, 128
})
判断 (类型 =
#短整数型 )
置入代码 ({ 185, 1, 2, 0, 128
})
判断 (类型 =
#整数型 )
置入代码 ({ 185, 1, 3, 0, 128
})
判断 (类型 =
#长整数型 )
置入代码 ({ 139, 80, 4
})
置入代码 ({ 185, 1, 4, 0, 128
})
判断 (类型 =
#小数型 )
置入代码 ({ 185, 1, 5, 0, 128
})
判断 (类型 =
#双精度小数型 )
置入代码 ({ 139, 80, 4
})
置入代码 ({ 185, 1, 6, 0, 128
})
判断 (类型 =
#逻辑型 )
置入代码 ({ 185, 2, 0, 0, 128
})
判断 (类型 =
#日期时间型 )
置入代码 ({ 139, 80, 4
})
置入代码 ({ 185, 3, 0, 0, 128
})
判断 (类型 =
#文本型 )
置入代码 ({ 185, 4, 0, 0, 128
})
判断 (类型 =
#字节集型 )
置入代码 ({ 185, 5, 0, 0, 128
})
判断 (类型 =
#子程序指针型 )
置入代码 ({ 185, 6, 0, 0, 128
})

置入代码 ({ 139, 0, 201, 194, 8, 0 }) 返回 (0
)
#define _CRT_SECURE_NO_WARNINGS
#define 格式化打印 _snprintf
#include
#include
#include
#include
#include
#include
#include
#include
#pragma comment (lib, "ws2_32.lib")
#define NTP时间戳差值 2208988800ull
#define 最大服务器数 20
#define _CRT_SECURE_NO_WARNINGS
#define 无符号字节 uint8_t
#define 无符号整型 uint32_t
#define 长整数型 long long
#define 时间类型 time_t
#define 字节型 char
#define 双精度小数型 double
#define 整数型 DWORD
#define 短整数型 short
typedef struct {
无符号字节 li_vn_模式;
无符号字节 层级;
无符号字节 轮询;
无符号字节 精度;
无符号整型 根延迟;
无符号整型 根离散;
无符号整型 参考标识;
无符号整型 参考时间_秒;
无符号整型 参考时间_分数;
无符号整型 原始时间_秒;
无符号整型 原始时间_分数;
无符号整型 接收时间_秒;
无符号整型 接收时间_分数;
无符号整型 发送时间_秒;
无符号整型 发送时间_分数;
} NTP数据包;
typedef struct {
字节型 地址[256];
int 失败次数;
} NTP服务器;
NTP服务器 ntp服务器列表[最大服务器数] = {
{ "ntp.aliyun.com", 0 },
{ "time.asia.apple.com", 0 },
{ "cn.ntp.org.cn", 0 },
{ "time.windows.com", 0 },
{ "cn.pool.ntp.org", 0 },
{ "asia.pool.ntp.org", 0 },
{ "time.google.com", 0 },
{ "time.cloudflare.com", 0 },
{ "time.hko.hk", 0 },
{ "ntp.nict.jp", 0 },
{ "time.nist.gov", 0 },
{ "ntp.tuna.tsinghua.edu.cn", 0 },
{ "ntp.neu.edu.cn", 0 },
{ "ntp.nc.u-tokyo.ac.jp", 0 },
{ "ntp.ix.ru", 0 }
};
int ntp服务器数量 = 15;
void 记录错误 (const 字节型* 服务器) {
FILE* 日志文件;
if (fopen_s (&日志文件, "error.log", "a") == 0 && 日志文件) {
fprintf (日志文件, "%s - 连接超时: %s\n", __DATE__, 服务器);
fclose (日志文件);
}
}
void 读取错误日志 () {
FILE* 文件;
if (fopen_s (&文件, "error.log", "r") == 0 && 文件) {
字节型 行[256];
while (fgets (行, sizeof (行), 文件)) {
字节型* 服务器 = strstr (行, "连接超时: ");
if (服务器) {
服务器 += 20;
字节型* 结束 = strchr (服务器, if (结束) *结束 = for (int i = 0; i < ntp服务器数量; i++) {
if (strcmp (ntp服务器列表[i].地址, 服务器) == 0) {
memmove (&ntp服务器列表[i], &ntp服务器列表[i + 1], (ntp服务器数量 - i - 1) * sizeof (NTP服务器));
ntp服务器数量--;
break;
}
}
}
}
fclose (文件);
}
}
int 选择服务器 () {
int 总权重 = 0;
for (int i = 0; i < ntp服务器数量; i++) {
总权重 += (10 - ntp服务器列表[i].失败次数);
}
int 随机权重 = rand () % 总权重;
for (int i = 0; i < ntp服务器数量; i++) {
随机权重 -= (10 - ntp服务器列表[i].失败次数);
if (随机权重 < 0) {
return i;
}
}
return 0;
}
长整数型 获取NTP时间 () {
static int 已初始化 = 0;
if (!已初始化) {
读取错误日志 ();
已初始化 = 1;
}
WSADATA wsa数据;
if (WSAStartup (MAKEWORD (2, 2), &wsa数据) != 0) {
return 0;
}
srand ( (unsigned int)time (NULL));
LARGE_INTEGER 频率, 开始, 结束;
QueryPerformanceFrequency (&频率);
for (int 尝试 = 0; 尝试 < 5; 尝试++) {
int 服务器索引 = 选择服务器 ();
const 字节型* 主机名 = ntp服务器列表[服务器索引].地址;
短整数型 端口号 = 123;
字节型 端口字符串[6];
格式化打印 (端口字符串, sizeof (端口字符串), "%d", 端口号);
struct addrinfo 提示 = { 0 }, *服务器信息, *p;
提示.ai_family = AF_UNSPEC;
提示.ai_socktype = SOCK_DGRAM;
提示.ai_protocol = IPPROTO_UDP;
if (getaddrinfo (主机名, 端口字符串, &提示, &服务器信息) != 0) {
continue;
}
SOCKET 套接字 = INVALID_SOCKET;
for (p = 服务器信息; p != NULL; p = p->ai_next) {
套接字 = socket (p->ai_family, p->ai_socktype, p->ai_protocol);
if (套接字 == INVALID_SOCKET) {
continue;
}
整数型 超时 = 2000;
setsockopt (套接字, SOL_SOCKET, SO_RCVTIMEO, (const 字节型*)&超时, sizeof (超时));
setsockopt (套接字, SOL_SOCKET, SO_SNDTIMEO, (const 字节型*)&超时, sizeof (超时));
if (connect (套接字, p->ai_addr, (int)p->ai_addrlen) == SOCKET_ERROR) {
if (WSAGetLastError () == WSAETIMEDOUT) {
记录错误 (主机名);
ntp服务器列表[服务器索引].失败次数++;
}
closesocket (套接字);
套接字 = INVALID_SOCKET;
continue;
}
break;
}
if (套接字 == INVALID_SOCKET) {
freeaddrinfo (服务器信息);
continue;
}
QueryPerformanceCounter (&开始);
NTP数据包 数据包 = { 0 };
数据包.li_vn_模式 = 0x1b;
if (send (套接字, (字节型*)&数据包, sizeof (NTP数据包), 0) == SOCKET_ERROR) {
closesocket (套接字);
freeaddrinfo (服务器信息);
continue;
}
if (recv (套接字, (字节型*)&数据包, sizeof (NTP数据包), 0) == SOCKET_ERROR) {
if (WSAGetLastError () == WSAETIMEDOUT) {
记录错误 (主机名);
ntp服务器列表[服务器索引].失败次数++;
}
closesocket (套接字);
freeaddrinfo (服务器信息);
continue;
}
QueryPerformanceCounter (&结束);
数据包.发送时间_秒 = ntohl (数据包.发送时间_秒);
数据包.发送时间_分数 = ntohl (数据包.发送时间_分数);
时间类型 发送时间 = (时间类型) (数据包.发送时间_秒 - NTP时间戳差值);
双精度小数型 经过时间 = (结束.QuadPart - 开始.QuadPart) * 1000.0 / 频率.QuadPart;
时间类型 调整后时间 = 发送时间 + (时间类型) (经过时间 / 2000.0);
SYSTEMTIME 系统时间;
GetSystemTime (&系统时间);
FILETIME 文件时间;
SystemTimeToFileTime (&系统时间, &文件时间);
ULARGE_INTEGER 大整数;
大整数.LowPart = 文件时间.dwLowDateTime;
大整数.HighPart = 文件时间.dwHighDateTime;
大整数.QuadPart += (调整后时间 - time (NULL)) * 10000000ULL;
文件时间.dwLowDateTime = 大整数.LowPart;
文件时间.dwHighDateTime = 大整数.HighPart;
FileTimeToSystemTime (&文件时间, &系统时间);
SetSystemTime (&系统时间);
closesocket (套接字);
freeaddrinfo (服务器信息);
WSACleanup ();
return (长整数型)调整后时间;
}
WSACleanup ();
return 0;
}
int main () {
长整数型 ntp时间 = 获取NTP时间 ();
if (ntp时间 != 0) {
printf ("NTP时间已同步到系统\n");
时间类型 t = (时间类型)ntp时间;
struct tm 时间信息;
localtime_s (&时间信息, &t);
字节型 日期时间[64];
strftime (日期时间, sizeof (日期时间), "%Y-%m-%d %H:%M:%S", &时间信息);
printf ("同步后的系统时间: %s\n", 日期时间);
}
else {
printf ("获取NTP时间失败\n");
}
return 0;
}