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