NTP(Network Time Protocol,網絡時間協議)是由RFC 1305定義的時間同步協議,用來在分佈式時間服務器和客戶端之間進行時間同步。NTP基於UDP報文進行傳輸,使用的UDP端口號爲123。
使用NTP的目的是對網絡內所有具有時鐘的設備進行時鐘同步,使網絡內所有設備的時鐘保持一致,從而使設備能夠提供基於統一時間的多種應用。
對於運行NTP的本地系統,既可以接收來自其他時鐘源的同步,又可以作爲時鐘源同步其他的時鐘,並且可以和其他設備互相同步。
其實NTP協議是很簡單的協議,據說很快就要被代替了,之前還寫過一個python解析flv協議文件代碼,這些應用層協議都是開放的,不是私有的,知道協議是很容易解析出的,不說廢話了
ntp.go
package main
import (
"bytes"
"encoding/binary"
)
const (
UNIX_STA_TIMESTAMP = 2208988800
)
/**
NTP協議 http://www.ntp.org/documentation.html
@author [email protected]
*/
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,
))
}
}
}