zoukankan      html  css  js  c++  java
  • golang ntp协议客户端

    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
    版本:%d
    模式:%d
    精度:%d
    轮询:%d
    系统精度:%d
    延时:%ds
    最大误差:%d
    时钟表示:%d
    时间戳:%d %d %d %d
    ",
    				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

  • 相关阅读:
    根据时间段查询数据并按照降序排列
    Json数组(以[ ] 中括号开头)字符串转为json对象
    Java 后台请求第三方系统接口详解
    Map 集合 和 String 字符串相互转换工具类
    并发工具类——CountDownLatch、CyclicBarrier、Semaphore与Exchanger
    AQS相关
    Atomic原子类
    CAS相关
    synchronized关键字相关
    谈谈对volatile关键字的理解
  • 原文地址:https://www.cnblogs.com/mengdejun/p/golang_ntp.html
Copyright © 2011-2022 走看看