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