1.由于有时候需要设置windos或Linux的时间,使用ntp自带的客户端总是不大方便,因此用go写了一个客户端。
下面是代码
package main
import (
"bytes"
"encoding/binary"
"errors"
"flag"
"fmt"
"log"
"net"
"os/exec"
"runtime"
"time"
)
func main() {
addr := flag.String("i", "cn.ntp.org.cn", "Ntp server address")
port := flag.Int("p", 123, "Ntp server port")
unix := flag.Bool("u", false, "Don't convert timestamp")
show := flag.Bool("s", false, "Show the detailed")
set := flag.Bool("set", false, "set system time")
flag.Parse()
ntp, err := NewNtp(*addr, *port)
if err != nil {
log.Fatal(err)
}
defer ntp.Close()
ntpTimeStamp, err := ntp.GetTimeStamp(!*unix, *show)
if err != nil {
log.Fatal(err)
} else if ntpTimeStamp > 0 {
var (
ntpTime = time.Unix(ntpTimeStamp, 0)
d = ntpTime.Format("2006-01-02")
t = ntpTime.Format("15:04:05")
)
if *set { // 设置系统时间
var cmd *exec.Cmd
if runtime.GOOS == `windows` {
cmd = exec.Command("cmd", "/c", fmt.Sprintf("date %s & time %s", d, t))
} else {
cmd = exec.Command("date", "-s", fmt.Sprintf("@%d", ntpTimeStamp))
}
fmt.Println(cmd.Args)
if err = cmd.Run(); err != nil {
log.Fatal(err)
}
} else {
fmt.Printf(`{"date":"%s","time":"%s","timestamp":%d}`, d, t, ntpTimeStamp)
fmt.Println()
}
}
}
// ntp的起始时间戳 1900年1月1日 0时0分0秒 2208988800
const UnixStaTimestamp = 2208988800
type (
ntp struct {
conn net.Conn // udp连接
data data // ntp封装数据
}
// NTP协议 http://www.ntp.org/documentation.html
data struct {
//1:32bits
Li uint8 //2 bits
Vn uint8 //3 bits
Mode uint8 //3 bits
Stratum uint8
Poll uint8
Precision uint8
RootDelay int32
RootDispersion int32
ReferenceIdentifier int32
ReferenceTimestamp uint64 //指示系统时钟最后一次校准的时间
OriginateTimestamp uint64 //指示客户向服务器发起请求的时间
ReceiveTimestamp uint64 //指服务器收到客户请求的时间
TransmitTimestamp uint64 //指示服务器向客户发时间戳的时间
}
)
func NewNtp(ip string, port int) (*ntp, error) {
var (
ntp = &ntp{data: data{Li: 0, Vn: 3, Mode: 3, Stratum: 0}}
err error
)
ntp.conn, err = net.Dial("udp", fmt.Sprintf("%s:%d", ip, port))
if err != nil {
return nil, err
}
return ntp, nil
}
func (my *ntp) Close() error {
return my.conn.Close()
}
func (my *ntp) GetTimeStamp(isUnix, show bool) (int64, error) {
//注意网络上使用的是大端字节排序
buf := &bytes.Buffer{}
head := (my.data.Li << 6) | (my.data.Vn << 3) | ((my.data.Mode << 5) >> 5)
binary.Write(buf, binary.BigEndian, uint8(head))
binary.Write(buf, binary.BigEndian, my.data.Stratum)
binary.Write(buf, binary.BigEndian, my.data.Poll)
binary.Write(buf, binary.BigEndian, my.data.Precision)
//写入其他字节数据
binary.Write(buf, binary.BigEndian, my.data.RootDelay)
binary.Write(buf, binary.BigEndian, my.data.RootDispersion)
binary.Write(buf, binary.BigEndian, my.data.ReferenceIdentifier)
binary.Write(buf, binary.BigEndian, my.data.ReferenceTimestamp)
binary.Write(buf, binary.BigEndian, my.data.OriginateTimestamp)
binary.Write(buf, binary.BigEndian, my.data.ReceiveTimestamp)
binary.Write(buf, binary.BigEndian, my.data.TransmitTimestamp)
ret, err := my.conn.Write(buf.Bytes())
if err != nil {
return 0, err
}
buffer := make([]byte, 2048)
ret, err = my.conn.Read(buffer)
if err != nil {
return 0, err
} else if ret <= 0 {
return 0, errors.New("return 0 byte")
}
var bit8 uint8
// 貌似这binary.Read只能顺序读,不能跳着读,想要跳着读只能使用切片bf
rb := bytes.NewReader(buffer)
binary.Read(rb, binary.BigEndian, &bit8)
// 向右偏移6位得到前两位LI即可
my.data.Li = bit8 >> 6
// 向右偏移2位,向右偏移5位,得到前中间3位
my.data.Vn = (bit8 << 2) >> 5
// 向左偏移5位,然后右偏移5位得到最后3位
my.data.Mode = (bit8 << 5) >> 5
binary.Read(rb, binary.BigEndian, &my.data.Stratum)
binary.Read(rb, binary.BigEndian, &my.data.Poll)
binary.Read(rb, binary.BigEndian, &my.data.Precision)
// 32bits
binary.Read(rb, binary.BigEndian, &my.data.RootDelay)
binary.Read(rb, binary.BigEndian, &my.data.RootDispersion)
binary.Read(rb, binary.BigEndian, &my.data.ReferenceIdentifier)
// 以下几个字段都是64位时间戳(NTP都是64位的时间戳)
binary.Read(rb, binary.BigEndian, &my.data.ReferenceTimestamp)
binary.Read(rb, binary.BigEndian, &my.data.OriginateTimestamp)
binary.Read(rb, binary.BigEndian, &my.data.ReceiveTimestamp)
binary.Read(rb, binary.BigEndian, &my.data.TransmitTimestamp)
// 转换为unix时间戳,先左偏移32位拿到64位时间戳的整数部分
if isUnix {
my.data.ReferenceTimestamp = (my.data.ReceiveTimestamp >> 32) - UnixStaTimestamp
if my.data.OriginateTimestamp > 0 {
my.data.OriginateTimestamp = (my.data.OriginateTimestamp >> 32) - UnixStaTimestamp
}
my.data.ReceiveTimestamp = (my.data.ReceiveTimestamp >> 32) - UnixStaTimestamp
my.data.TransmitTimestamp = (my.data.TransmitTimestamp >> 32) - UnixStaTimestamp
} else {
show = true // 不转换时间戳时只打印详细结果
}
if show {
fmt.Printf("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",
my.data.Li,
my.data.Vn,
my.data.Mode,
my.data.Stratum,
my.data.Poll,
my.data.Precision,
my.data.RootDelay,
my.data.RootDispersion,
my.data.ReferenceIdentifier,
my.data.ReferenceTimestamp,
my.data.OriginateTimestamp,
my.data.ReceiveTimestamp,
my.data.TransmitTimestamp,
)
return 0, nil
} else if my.data.ReferenceTimestamp == my.data.ReceiveTimestamp &&
my.data.ReceiveTimestamp == my.data.TransmitTimestamp &&
my.data.OriginateTimestamp == 0 {
return int64(my.data.ReferenceTimestamp), nil
}
return 0, errors.New("ntp return error")
}
2.下面是使用方法
# .\ntp_client.exe -h
Usage of .\ntp_client.exe:
-i string
Ntp server address (default "cn.ntp.org.cn")
-p int
Ntp server port (default 123)
-s Show the detailed
-set
set system time
-u Don't convert timestamp
上面是显示帮助,默认使用的ntp服务器可以去
http://www.ntp.org.cn/pool.php
上找到
# .\ntp_client.exe
{"date":"2018-10-25","time":"14:41:41","timestamp":1540449701}
上面是直接运行的结果
# .\ntp_client.exe -s
LI:0
版本:3
模式:4
精度:2
轮询:0
系统精度:231
延时:7s
最大误差:69
时钟表示:176764167
时间戳:1540449797 0 1540449797 1540449797
这样可以显示ntp返回的数据具体内容
# .\ntp_client.exe -set
[cmd /c date 2018-10-25 & time 14:43:46]
# ./ntp_client -set
[date -s @1540441156]
这样可以设置windos或Linux的系统时间
-u的参数是显示没有减去ntp的起始时间戳的时间戳,一般没啥用
请问博主的加载时动画和评论验证是怎么搞的啊
加载动画我已经忘记咋搞的了,太早之前弄得啊
。不过评论验证插件是我写的(http://forum.typecho.org/viewtopic.php?f=6&t=9825&p=37775&hilit=zj007#p37775)去论坛下载吧。