github.com/turingchain2020/turingchain@v1.1.21/common/ntp.go (about) 1 // Copyright Turing Corp. 2018 All Rights Reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package common 6 7 import ( 8 "encoding/binary" 9 "errors" 10 "math" 11 "net" 12 "sort" 13 "time" 14 15 log "github.com/turingchain2020/turingchain/common/log/log15" 16 ) 17 18 const ntpEpochOffset = 2208988800 19 20 // 如果获取的ntp时间和自己时间相差太大,超过阈值,保守起见,不采用 21 const safeDeltaScope = 300 * 1000 * int64(time.Millisecond) 22 23 //ErrNetWorkDealy error 24 var ErrNetWorkDealy = errors.New("ErrNetWorkDealy") 25 26 // NTP packet format (v3 with optional v4 fields removed) 27 // 28 // 0 1 2 3 29 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 30 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 31 // |LI | VN |Mode | Stratum | Poll | Precision | 32 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 33 // | Root Delay | 34 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 35 // | Root Dispersion | 36 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 37 // | Reference ID | 38 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 39 // | | 40 // + Reference Timestamp (64) + 41 // | | 42 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 43 // | | 44 // + Origin Timestamp (64) + 45 // | | 46 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 47 // | | 48 // + Receive Timestamp (64) + 49 // | | 50 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 51 // | | 52 // + Transmit Timestamp (64) + 53 // | | 54 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 55 // 56 type packet struct { 57 Settings uint8 // leap yr indicator, ver number, and mode 58 Stratum uint8 // stratum of local clock 59 Poll int8 // poll exponent 60 Precision int8 // precision exponent 61 RootDelay uint32 // root delay 62 RootDispersion uint32 // root dispersion 63 ReferenceID uint32 // reference id 64 RefTimeSec uint32 // reference timestamp sec 65 RefTimeFrac uint32 // reference timestamp fractional 66 OrigTimeSec uint32 // origin time secs 67 OrigTimeFrac uint32 // origin time fractional 68 RxTimeSec uint32 // receive time secs 69 RxTimeFrac uint32 // receive time frac 70 TxTimeSec uint32 // transmit time secs 71 TxTimeFrac uint32 // transmit time frac 72 } 73 74 /* 75 t2 t3 t6 t7 76 --------------------------------------------------------- 77 /\ \ /\ \ 78 / \ / \ 79 / \ / \ 80 / \/ / \/ 81 --------------------------------------------------------- 82 t1 t4 t5 t8 83 */ 84 85 //GetNtpTime 利用服务器返回的 t2, t3, 和本地的 t1, t4 校准时间 86 //delt = ((t2-t1)+(t3-t4))/2 87 //current = t4 + delt 88 func GetNtpTime(host string) (time.Time, error) { 89 90 // Setup a UDP connection 91 conn, err := net.Dial("udp", host) 92 if err != nil { 93 return time.Time{}, err 94 } 95 defer conn.Close() 96 if err := conn.SetDeadline(time.Now().Add(3 * time.Second)); err != nil { 97 return time.Time{}, err 98 } 99 // configure request settings by specifying the first byte as 100 // 00 011 011 (or 0x1B) 101 // | | +-- client mode (3) 102 // | + ----- version (3) 103 // + -------- leap year indicator, 0 no warning 104 req := &packet{Settings: 0x1B} 105 t1 := time.Now() 106 // send time request 107 if err := binary.Write(conn, binary.BigEndian, req); err != nil { 108 return time.Time{}, err 109 } 110 111 // block to receive server response 112 rsp := &packet{} 113 if err := binary.Read(conn, binary.BigEndian, rsp); err != nil { 114 return time.Time{}, err 115 } 116 t2 := intToTime(rsp.RxTimeSec, rsp.RxTimeFrac) 117 t3 := intToTime(rsp.TxTimeSec, rsp.TxTimeFrac) 118 t4 := time.Now() 119 // On POSIX-compliant OS, time is expressed 120 // using the Unix time epoch (or secs since year 1970). 121 // NTP seconds are counted since 1900 and therefore must 122 // be corrected with an epoch offset to convert NTP seconds 123 // to Unix time by removing 70 yrs of seconds (1970-1900) 124 // or 2208988800 seconds. 125 /* 126 js, _ := json.MarshalIndent(rsp, "", "\t") 127 fmt.Println(string(js)) 128 */ 129 //t2 - t1 -> deltaNet + deltaTime = d1 130 //t3 - t4 -> -deltaNet + deltaTime = d2 131 //如果deltaNet相同 132 //判断t2 - t1 和 t3 - t4 绝对值的 倍数,如果超过2倍,认为无效(请求和回复延时严重不对称) 133 d1 := t2.Sub(t1) 134 d2 := t3.Sub(t4) 135 delt := (d1 + d2) / 2 136 if delt > time.Duration(safeDeltaScope) || delt < time.Duration(-safeDeltaScope) { 137 log.Error("GetNtpTime", "host", host, "delt", delt, "RxSec", rsp.RxTimeSec, "RxNs", rsp.RxTimeFrac, "TxSec", rsp.TxTimeSec, "TxNs", rsp.TxTimeFrac) 138 log.Error("GetNtpTime", "delt", delt, "t1", t1, "t2", t2, "t3", t3, "now", t4, "d1", d1, "d2", d2) 139 return time.Time{}, errors.New("WrongNtpDelteTime") 140 } 141 return t4.Add(delt), nil 142 } 143 144 //n timeserver 145 //n/2+1 is ok and the same 146 type durationSlice []time.Duration 147 148 func (s durationSlice) Len() int { return len(s) } 149 func (s durationSlice) Less(i, j int) bool { return abs(s[i]) < abs(s[j]) } 150 func (s durationSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 151 152 //GetRealTime 获取实际时间 153 func GetRealTime(hosts []string) time.Time { 154 q := len(hosts)/2 + 1 155 //q := 5 156 ch := make(chan time.Duration, len(hosts)) 157 for i := 0; i < len(hosts); i++ { 158 go func(host string) { 159 ntptime, err := getTimeRetry(host, 1) 160 if ntptime.IsZero() || err != nil { 161 log.Error("getTimeRetry", "host", host, "err", err) 162 ch <- time.Duration(math.MaxInt64) 163 } else { 164 dt := time.Until(ntptime) 165 log.Info("getTimeRetry", "host", host, "dt", dt) 166 ch <- dt 167 } 168 }(hosts[i]) 169 } 170 dtlist := make([]time.Duration, 0) 171 for i := 0; i < len(hosts); i++ { 172 t := <-ch 173 if t == time.Duration(math.MaxInt64) { 174 continue 175 } 176 dtlist = append(dtlist, t) 177 if len(dtlist) >= q { 178 calclist := make([]time.Duration, len(dtlist)) 179 copy(calclist, dtlist) 180 sort.Sort(durationSlice(calclist)) 181 calclist = maxSubList(calclist, time.Millisecond*100) 182 if len(calclist) < q { 183 continue 184 } 185 drift := time.Duration(0) 186 for i := 0; i < len(calclist); i++ { 187 drift += calclist[i] 188 } 189 return time.Now().Add(drift / time.Duration(len(calclist))) 190 } 191 } 192 return time.Time{} 193 } 194 195 func abs(t time.Duration) time.Duration { 196 if t < 0 { 197 return -t 198 } 199 return t 200 } 201 202 func maxSubList(list []time.Duration, dt time.Duration) (sub []time.Duration) { 203 if len(list) == 0 { 204 return list 205 } 206 var start int 207 var next int 208 for i := 0; i < len(list); i++ { 209 var nextTime time.Duration 210 next = i + 1 211 if next == len(list) { 212 nextTime = math.MaxInt64 213 } else { 214 nextTime = list[next] 215 } 216 if abs(nextTime-list[i]) > dt { 217 if len(sub) < (next-start) && (next-start) > 1 { 218 sub = list[start:next] 219 } 220 start = next 221 } 222 } 223 return sub 224 } 225 226 //GetRealTimeRetry 重试获取实际时间 227 func GetRealTimeRetry(hosts []string, retry int) time.Time { 228 for i := 0; i < retry; i++ { 229 t := GetRealTime(hosts) 230 if !t.IsZero() { 231 return t 232 } 233 } 234 return time.Time{} 235 } 236 237 func getTimeRetry(host string, retry int) (time.Time, error) { 238 var lasterr error 239 for i := 0; i < retry; i++ { 240 t, err := GetNtpTime(host) 241 if err != nil { 242 lasterr = err 243 if i < retry-1 { 244 //have a rest 245 time.Sleep(time.Millisecond * 10) 246 continue 247 } 248 return time.Time{}, err 249 } 250 return t, nil 251 } 252 return time.Time{}, lasterr 253 } 254 255 func intToTime(sec, frac uint32) time.Time { 256 secs := int64(sec) - int64(ntpEpochOffset) 257 nanos := (int64(frac) * 1e9) >> 32 258 return time.Unix(secs, nanos) 259 }