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  }