github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/p2p/discv5/ntp.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 12:09:43</date> 10 //</624342657220481024> 11 12 13 //包含通过sntp协议进行的ntp时间漂移检测: 14 //https://tools.ietf.org/html/rfc4330 15 16 package discv5 17 18 import ( 19 "fmt" 20 "net" 21 "sort" 22 "strings" 23 "time" 24 25 "github.com/ethereum/go-ethereum/log" 26 ) 27 28 const ( 29 ntpPool = "pool.ntp.org" //ntppool是要查询当前时间的ntp服务器 30 ntpChecks = 3 //要对NTP服务器执行的测量数 31 ) 32 33 //DurationSicle将Sort.Interface方法附加到[]Time.Duration, 34 //按递增顺序排序。 35 type durationSlice []time.Duration 36 37 func (s durationSlice) Len() int { return len(s) } 38 func (s durationSlice) Less(i, j int) bool { return s[i] < s[j] } 39 func (s durationSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 40 41 //checkclockfloft查询NTP服务器的时钟偏移,并警告用户 42 //检测到一个足够大的。 43 func checkClockDrift() { 44 drift, err := sntpDrift(ntpChecks) 45 if err != nil { 46 return 47 } 48 if drift < -driftThreshold || drift > driftThreshold { 49 warning := fmt.Sprintf("System clock seems off by %v, which can prevent network connectivity", drift) 50 howtofix := fmt.Sprintf("Please enable network time synchronisation in system settings") 51 separator := strings.Repeat("-", len(warning)) 52 53 log.Warn(separator) 54 log.Warn(warning) 55 log.Warn(howtofix) 56 log.Warn(separator) 57 } else { 58 log.Debug(fmt.Sprintf("Sanity NTP check reported %v drift, all ok", drift)) 59 } 60 } 61 62 //sntpdrift针对ntp服务器执行幼稚的时间解析,并返回 63 //测量漂移。此方法使用简单版本的NTP。不太准确 64 //但就这些目的而言应该是好的。 65 // 66 //注意,与请求的数量相比,它执行两个额外的测量 67 //一种能够将这两个极端作为离群值丢弃的方法。 68 func sntpDrift(measurements int) (time.Duration, error) { 69 //解析NTP服务器的地址 70 addr, err := net.ResolveUDPAddr("udp", ntpPool+":123") 71 if err != nil { 72 return 0, err 73 } 74 //构造时间请求(仅设置两个字段的空包): 75 //位3-5:协议版本,3 76 //位6-8:操作模式,客户机,3 77 request := make([]byte, 48) 78 request[0] = 3<<3 | 3 79 80 //执行每个测量 81 drifts := []time.Duration{} 82 for i := 0; i < measurements+2; i++ { 83 //拨号NTP服务器并发送时间检索请求 84 conn, err := net.DialUDP("udp", nil, addr) 85 if err != nil { 86 return 0, err 87 } 88 defer conn.Close() 89 90 sent := time.Now() 91 if _, err = conn.Write(request); err != nil { 92 return 0, err 93 } 94 //检索回复并计算经过的时间 95 conn.SetDeadline(time.Now().Add(5 * time.Second)) 96 97 reply := make([]byte, 48) 98 if _, err = conn.Read(reply); err != nil { 99 return 0, err 100 } 101 elapsed := time.Since(sent) 102 103 //从回复数据中重建时间 104 sec := uint64(reply[43]) | uint64(reply[42])<<8 | uint64(reply[41])<<16 | uint64(reply[40])<<24 105 frac := uint64(reply[47]) | uint64(reply[46])<<8 | uint64(reply[45])<<16 | uint64(reply[44])<<24 106 107 nanosec := sec*1e9 + (frac*1e9)>>32 108 109 t := time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC).Add(time.Duration(nanosec)).Local() 110 111 //根据假定的响应时间rrt/2计算漂移 112 drifts = append(drifts, sent.Sub(t)+elapsed/2) 113 } 114 //计算平均干重(减少两端以避免异常值) 115 sort.Sort(durationSlice(drifts)) 116 117 drift := time.Duration(0) 118 for i := 1; i < len(drifts)-1; i++ { 119 drift += drifts[i] 120 } 121 return drift / time.Duration(measurements), nil 122 } 123