NTP(Network Time Protocol,网络时间协议)是由RFC 1305定义的时间同步协议,用来在分布式时间服务器和客户端之间进行时间同步。NTP基于UDP报文进行传输,使用的UDP端口号为123。
使用NTP的目的是对网络内所有具有时钟的设备进行时钟同步,使网络内所有设备的时钟保持一致,从而使设备能够提供基于统一时间的多种应用。
对于运行NTP的本地系统,既可以接收来自其他时钟源的同步,又可以作为时钟源同步其他的时钟,并且可以和其他设备互相同步。
相信文本介绍看多也厌倦了,就不多废话,直接上干活
ntp.go
package main import ( "bytes" "encoding/binary" ) const ( UNIX_STA_TIMESTAMP = 2208988800 ) /** NTP协议 http://www.ntp.org/documentation.html @author mengdj@outlook.com */ type Ntp struct { //1:32bits Li uint8 //2 bits Vn uint8 //3 bits Mode uint8 //3 bits Stratum uint8 Poll uint8 Precision uint8 //2: RootDelay int32 RootDispersion int32 ReferenceIdentifier int32 //64位时间戳 ReferenceTimestamp uint64 //指示系统时钟最后一次校准的时间 OriginateTimestamp uint64 //指示客户向服务器发起请求的时间 ReceiveTimestamp uint64 //指服务器收到客户请求的时间 TransmitTimestamp uint64 //指示服务器向客户发时间戳的时间 } func NewNtp() (p *Ntp) { //其他参数通常都是服务器返回的 p = &Ntp{Li: 0, Vn: 3, Mode: 3, Stratum: 0} return p } /** 构建NTP协议信息 */ func (this *Ntp) GetBytes() []byte { //注意网络上使用的是大端字节排序 buf := &bytes.Buffer{} head := (this.Li << 6) | (this.Vn << 3) | ((this.Mode << 5) >> 5) binary.Write(buf, binary.BigEndian, uint8(head)) binary.Write(buf, binary.BigEndian, this.Stratum) binary.Write(buf, binary.BigEndian, this.Poll) binary.Write(buf, binary.BigEndian, this.Precision) //写入其他字节数据 binary.Write(buf, binary.BigEndian, this.RootDelay) binary.Write(buf, binary.BigEndian, this.RootDispersion) binary.Write(buf, binary.BigEndian, this.ReferenceIdentifier) binary.Write(buf, binary.BigEndian, this.ReferenceTimestamp) binary.Write(buf, binary.BigEndian, this.OriginateTimestamp) binary.Write(buf, binary.BigEndian, this.ReceiveTimestamp) binary.Write(buf, binary.BigEndian, this.TransmitTimestamp) //[27 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] return buf.Bytes() } func (this *Ntp) Parse(bf []byte, useUnixSec bool) { var ( bit8 uint8 bit32 int32 bit64 uint64 rb *bytes.Reader ) //貌似这binary.Read只能顺序读,不能跳着读,想要跳着读只能使用切片bf rb = bytes.NewReader(bf) binary.Read(rb, binary.BigEndian, &bit8) //向右偏移6位得到前两位LI即可 this.Li = bit8 >> 6 //向右偏移2位,向右偏移5位,得到前中间3位 this.Vn = (bit8 << 2) >> 5 //向左偏移5位,然后右偏移5位得到最后3位 this.Mode = (bit8 << 5) >> 5 binary.Read(rb, binary.BigEndian, &bit8) this.Stratum = bit8 binary.Read(rb, binary.BigEndian, &bit8) this.Poll = bit8 binary.Read(rb, binary.BigEndian, &bit8) this.Precision = bit8 //32bits binary.Read(rb, binary.BigEndian, &bit32) this.RootDelay = bit32 binary.Read(rb, binary.BigEndian, &bit32) this.RootDispersion = bit32 binary.Read(rb, binary.BigEndian, &bit32) this.ReferenceIdentifier = bit32 //以下几个字段都是64位时间戳(NTP都是64位的时间戳) binary.Read(rb, binary.BigEndian, &bit64) this.ReferenceTimestamp = bit64 binary.Read(rb, binary.BigEndian, &bit64) this.OriginateTimestamp = bit64 binary.Read(rb, binary.BigEndian, &bit64) this.ReceiveTimestamp = bit64 binary.Read(rb, binary.BigEndian, &bit64) this.TransmitTimestamp = bit64 //转换为unix时间戳,先左偏移32位拿到64位时间戳的整数部分,然后ntp的起始时间戳 1900年1月1日 0时0分0秒 2208988800 if useUnixSec { this.ReferenceTimestamp = (this.ReceiveTimestamp >> 32) - UNIX_STA_TIMESTAMP if this.OriginateTimestamp > 0 { this.OriginateTimestamp = (this.OriginateTimestamp >> 32) - UNIX_STA_TIMESTAMP } this.ReceiveTimestamp = (this.ReceiveTimestamp >> 32) - UNIX_STA_TIMESTAMP this.TransmitTimestamp = (this.TransmitTimestamp >> 32) - UNIX_STA_TIMESTAMP } }
main.go
package main import ( "fmt" "log" "net" ) func main() { var ( ntp *Ntp buffer []byte err error ret int ) //链接阿里云NTP服务器,NTP有很多免费服务器可以使用time.windows.com conn, err := net.Dial("udp", "ntp1.aliyun.com:123") defer func() { if err := recover(); err != nil { log.Println(err) } conn.Close() }() ntp = NewNtp() conn.Write(ntp.GetBytes()) buffer = make([]byte, 2048) ret, err = conn.Read(buffer) if err == nil { if ret > 0 { ntp.Parse(buffer, true) fmt.Println(fmt.Sprintf( "LI:%d\r\n版本:%d\r\n模式:%d\r\n精度:%d\r\n轮询:%d\r\n系统精度:%d\r\n延时:%ds\r\n最大误差:%d\r\n时钟表示:%d\r\n时间戳:%d %d %d %d\r\n", ntp.Li, ntp.Vn, ntp.Mode, ntp.Stratum, ntp.Poll, ntp.Precision, ntp.RootDelay, ntp.RootDispersion, ntp.ReferenceIdentifier, ntp.ReferenceTimestamp, ntp.OriginateTimestamp, ntp.ReceiveTimestamp, ntp.TransmitTimestamp, )) } } }
源码见:https://github.com/mengdj/ntp-go
有疑问加站长微信联系(非本文作者)