使用go对NTP发起请求获取当前时间

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()
}
posted @   厚礼蝎  阅读(85)  评论(0编辑  收藏  举报
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
历史上的今天:
2023-07-27 go实现对容器日志的读取并通过api展示
点击右上角即可分享
微信分享提示