package main
import (
"encoding/binary"
"fmt"
"log"
"net"
"time"
)
// Packet 定義一個結構體,用於表示 NTP 資料包
type Packet struct {
Settings uint8 // 設定欄位
Stratum uint8 // 層級欄位
Poll int8 // 輪詢欄位
Precision int8 // 精度欄位
RootDelay uint32 // 根延遲欄位
RootDispersion uint32 // 根離散欄位
ReferenceID uint32 // 參考 ID 欄位
RefTimeSec uint32 // 參考時間秒部分
RefTimeFrac uint32 // 參考時間分數部分
OrigTimeSec uint32 // 原始時間秒部分
OrigTimeFrac uint32 // 原始時間分數部分
RxTimeSec uint32 // 接收時間秒部分
RxTimeFrac uint32 // 接收時間分數部分
TxTimeSec uint32 // 傳輸時間秒部分
TxTimeFrac uint32 // 傳輸時間分數部分
}
func NewPacket() *Packet {
return &Packet{}
}
func NewPacketSettings(settings uint8) *Packet {
return &Packet{Settings: settings}
}
// 定義 NTP 紀元時間與 UNIX 紀元時間的偏移量
const ntpEpochOffset = 2208988800
var (
host = "pool.ntp.org"
timeout = 15 * time.Second
)
// sendRequest 函式用於傳送 NTP 請求
func sendRequest(conn net.Conn, req *Packet) error {
return binary.Write(conn, binary.BigEndian, req) // 以大端位元組序將請求包寫入連線
}
// readResponse 函式用於讀取 NTP 響應
func readResponse(conn net.Conn, rsp *Packet) error {
return binary.Read(conn, binary.BigEndian, rsp) // 以大端位元組序從連線讀取響應包
}
// parseTime 函式用於解析 NTP 響應中的時間
func parseTime(rsp *Packet) time.Time {
secs := float64(rsp.TxTimeSec) - ntpEpochOffset // 計算自 UNIX 紀元的秒數
nanos := (int64(rsp.TxTimeFrac) * 1e9) >> 32 // 計算秒的小數部分
return time.Unix(int64(secs), nanos) // 返回完整的時間
}
// GetCurrentTime Ntp 函式用於從 NTP 伺服器獲取當前時間
func GetCurrentTime() {
// 建立一個 UDP 連線到指定的 NTP 伺服器
conn, err := net.Dial("udp", fmt.Sprintf("%s:123", host))
if err != nil {
log.Fatalf("Failed to connect: %v", err) // 連線失敗則記錄錯誤並退出
}
defer conn.Close() // 函式結束時關閉連線
// 設定連線的讀寫超時時間為 15 秒
if err := conn.SetDeadline(time.Now().Add(timeout)); err != nil {
log.Fatalf("Failed to set deadline: %v", err) // 設定超時失敗則記錄錯誤並退出
}
req := NewPacketSettings(0x1B) // 建立一個 NTP 請求包
// 傳送請求包到 NTP 伺服器
if err := sendRequest(conn, req); err != nil {
log.Fatalf("Failed to send request: %v", err) // 傳送請求失敗則記錄錯誤並退出
}
rsp := NewPacket() // 建立一個空的 NTP 響應包
// 從 NTP 伺服器讀取響應包
if err := readResponse(conn, rsp); err != nil {
log.Fatalf("Failed to read server response: %v", err) // 讀取響應失敗則記錄錯誤並退出
}
currentTime := parseTime(rsp) // 解析響應包中的時間
fmt.Printf("Current time: %v\n", currentTime) // 列印當前時間
fmt.Println(currentTime.Format("2006-01-02 15:04:05")) // 或者格式化列印
}
func main() {
GetCurrentTime()
}