golang ntp协议客户端

NTP(Network Time Protocol,网络时间协议)是由RFC 1305定义的时间同步协议,用来在分布式时间服务器和客户端之间进行时间同步。NTP基于UDP报文进行传输,使用的UDP端口号为123。

使用NTP的目的是对网络内所有具有时钟的设备进行时钟同步,使网络内所有设备的时钟保持一致,从而使设备能够提供基于统一时间的多种应用。

对于运行NTP的本地系统,既可以接收来自其他时钟源的同步,又可以作为时钟源同步其他的时钟,并且可以和其他设备互相同步。

 

相信文本介绍看多也厌倦了,就不多废话,直接上干活

ntp.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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

posted @   小孟文摘  阅读(1072)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示