github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/p2p/discv5/ntp.go (about)

     1  
     2  //此源码被清华学神尹成大魔王专业翻译分析并修改
     3  //尹成QQ77025077
     4  //尹成微信18510341407
     5  //尹成所在QQ群721929980
     6  //尹成邮箱 yinc13@mails.tsinghua.edu.cn
     7  //尹成毕业于清华大学,微软区块链领域全球最有价值专家
     8  //https://mvp.microsoft.com/zh-cn/PublicProfile/4033620
     9  //版权所有2016 Go Ethereum作者
    10  //此文件是Go以太坊库的一部分。
    11  //
    12  //Go-Ethereum库是免费软件:您可以重新分发它和/或修改
    13  //根据GNU发布的较低通用公共许可证的条款
    14  //自由软件基金会,或者许可证的第3版,或者
    15  //(由您选择)任何更高版本。
    16  //
    17  //Go以太坊图书馆的发行目的是希望它会有用,
    18  //但没有任何保证;甚至没有
    19  //适销性或特定用途的适用性。见
    20  //GNU较低的通用公共许可证,了解更多详细信息。
    21  //
    22  //你应该收到一份GNU较低级别的公共许可证副本
    23  //以及Go以太坊图书馆。如果没有,请参见<http://www.gnu.org/licenses/>。
    24  
    25  //包含通过sntp协议进行的ntp时间漂移检测:
    26  //https://tools.ietf.org/html/rfc4330
    27  
    28  package discv5
    29  
    30  import (
    31  	"fmt"
    32  	"net"
    33  	"sort"
    34  	"strings"
    35  	"time"
    36  
    37  	"github.com/ethereum/go-ethereum/log"
    38  )
    39  
    40  const (
    41  ntpPool   = "pool.ntp.org" //ntppool是要查询当前时间的ntp服务器
    42  ntpChecks = 3              //要对NTP服务器执行的测量数
    43  )
    44  
    45  //DurationSicle将Sort.Interface方法附加到[]Time.Duration,
    46  //按递增顺序排序。
    47  type durationSlice []time.Duration
    48  
    49  func (s durationSlice) Len() int           { return len(s) }
    50  func (s durationSlice) Less(i, j int) bool { return s[i] < s[j] }
    51  func (s durationSlice) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
    52  
    53  //checkclockfloft查询NTP服务器的时钟偏移,并警告用户
    54  //检测到一个足够大的。
    55  func checkClockDrift() {
    56  	drift, err := sntpDrift(ntpChecks)
    57  	if err != nil {
    58  		return
    59  	}
    60  	if drift < -driftThreshold || drift > driftThreshold {
    61  		warning := fmt.Sprintf("System clock seems off by %v, which can prevent network connectivity", drift)
    62  		howtofix := fmt.Sprintf("Please enable network time synchronisation in system settings")
    63  		separator := strings.Repeat("-", len(warning))
    64  
    65  		log.Warn(separator)
    66  		log.Warn(warning)
    67  		log.Warn(howtofix)
    68  		log.Warn(separator)
    69  	} else {
    70  		log.Debug(fmt.Sprintf("Sanity NTP check reported %v drift, all ok", drift))
    71  	}
    72  }
    73  
    74  //sntpdrift针对ntp服务器执行幼稚的时间解析,并返回
    75  //测量漂移。此方法使用简单版本的NTP。不太准确
    76  //但就这些目的而言应该是好的。
    77  //
    78  //注意,与请求的数量相比,它执行两个额外的测量
    79  //一种能够将这两个极端作为离群值丢弃的方法。
    80  func sntpDrift(measurements int) (time.Duration, error) {
    81  //解析NTP服务器的地址
    82  	addr, err := net.ResolveUDPAddr("udp", ntpPool+":123")
    83  	if err != nil {
    84  		return 0, err
    85  	}
    86  //构造时间请求(仅设置两个字段的空包):
    87  //位3-5:协议版本,3
    88  //位6-8:操作模式,客户机,3
    89  	request := make([]byte, 48)
    90  	request[0] = 3<<3 | 3
    91  
    92  //执行每个测量
    93  	drifts := []time.Duration{}
    94  	for i := 0; i < measurements+2; i++ {
    95  //拨号NTP服务器并发送时间检索请求
    96  		conn, err := net.DialUDP("udp", nil, addr)
    97  		if err != nil {
    98  			return 0, err
    99  		}
   100  		defer conn.Close()
   101  
   102  		sent := time.Now()
   103  		if _, err = conn.Write(request); err != nil {
   104  			return 0, err
   105  		}
   106  //检索回复并计算经过的时间
   107  		conn.SetDeadline(time.Now().Add(5 * time.Second))
   108  
   109  		reply := make([]byte, 48)
   110  		if _, err = conn.Read(reply); err != nil {
   111  			return 0, err
   112  		}
   113  		elapsed := time.Since(sent)
   114  
   115  //从回复数据中重建时间
   116  		sec := uint64(reply[43]) | uint64(reply[42])<<8 | uint64(reply[41])<<16 | uint64(reply[40])<<24
   117  		frac := uint64(reply[47]) | uint64(reply[46])<<8 | uint64(reply[45])<<16 | uint64(reply[44])<<24
   118  
   119  		nanosec := sec*1e9 + (frac*1e9)>>32
   120  
   121  		t := time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC).Add(time.Duration(nanosec)).Local()
   122  
   123  //根据假定的响应时间rrt/2计算漂移
   124  		drifts = append(drifts, sent.Sub(t)+elapsed/2)
   125  	}
   126  //计算平均干重(减少两端以避免异常值)
   127  	sort.Sort(durationSlice(drifts))
   128  
   129  	drift := time.Duration(0)
   130  	for i := 1; i < len(drifts)-1; i++ {
   131  		drift += drifts[i]
   132  	}
   133  	return drift / time.Duration(measurements), nil
   134  }