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