本文實現基於RFC 868「Time Protocol」的系統時間同步客戶端。協議極簡:TCP連接端口37,服務器發送4字節大端序無符號長整型,表示自1900-01-01 00:00:00 UTC以來的秒數。無需請求,連接即得時間。
Windows FILETIME 以1601-01-01為紀元,100納秒為單位。轉換鏈路:
ULONG ulTime (秒, 1900紀元)
→ SYSTEMTIME (1900-01-01 00:00:00)
→ FILETIME (1601紀元, 100ns單位)
→ 加上 ulTime * 10,000,000 (1秒 = 10^7 * 100ns)
→ SYSTEMTIME (目標時間)
→ SetSystemTime()
void setTimeFromTP(ULONG ulTime) {
FILETIME ft;
SYSTEMTIME st = {1900, 1, 0, 1, 0, 0, 0, 0}; // 1900-01-01
SystemTimeToFileTime(&st, &ft);
LONGLONG* pLLong = (LONGLONG*)&ft;
*pLLong += (LONGLONG)10000000 * ulTime; // 秒轉100ns單位
FileTimeToSystemTime(&ft, &st);
::SetSystemTime(&st);
}
SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
sockaddr_in servAddr = {AF_INET, htons(37), inet_addr("69.25.96.13")};
connect(s, (sockaddr*)&servAddr, sizeof(servAddr));
ULONG ulTime = 0;
recv(s, (char*)&ulTime, sizeof(ulTime), 0);
ulTime = ntohl(ulTime); // 網絡序(大端) → 主機序(小端)
setTimeFromTP(ulTime);
SetSystemTime 需要管理員權限/SE_SYSTEMTIME_NAME特權| 協議 | 精度 | 複雜度 | 適用場景 |
|---|---|---|---|
| RFC 868 Time | 1秒 | 極低 | 嵌入式、舊系統、簡單同步 |
| NTP (RFC 5905) | 毫秒級 | 中等 | 通用服務器/桌面 |
| PTP (IEEE 1588) | 亞微秒級 | 高 | 金融、電信、工控 |
這個百行代碼完整演示了「協議理解 → 時間制轉換 → 網絡編程 → 系統調用」的全鏈路。雖然RFC 868已過時,但其簡潔性使其成為學習網絡時間同步、Windows時間API、字節序處理的絕佳教具。生產環境請優先選用NTP庫(如Windows Time Service、chrony、ntpd)。