zoukankan      html  css  js  c++  java
  • 通过NTP协议进行时间同步

    最近发现手机的时间不是很准了,便到网上下了一个同步时间的小程序,简单了看了一下它的原理,是通过NTP协议来实现校时的,就顺便学习了一下NTP协议,用C#写了个简单的实现。

    NTP(Network Time Protocol,网络时间协议)是由RFC 1305定义的时间同步协议,用来在分布式时间服务器和客户端之间进行时间同步。

    NTP工作原理

    NTP的基本工作原理如下图所示。Device A和Device B通过网络相连,它们都有自己独立的系统时钟,需要通过NTP实现各自系统时钟的自动同步。为便于理解,作如下假设:

    • 在Device A和Device B的系统时钟同步之前,Device A的时钟设定为10:00:00am,Device B的时钟设定为11:00:00am。
    • Device B作为NTP时间服务器,即Device A将使自己的时钟与Device B的时钟同步。
    • NTP报文在Device A和Device B之间单向传输所需要的时间为1秒。

            

    • Device A发送一个NTP报文给Device B,该报文带有它离开Device A时的时间戳,该时间戳为10:00:00am(T1)。
    • 当此NTP报文到达Device B时,Device B加上自己的时间戳,该时间戳为11:00:01am(T2)。
    • 当此NTP报文离开Device B时,Device B再加上自己的时间戳,该时间戳为11:00:02am(T3)。
    • 当Device A接收到该响应报文时,Device A的本地时间为10:00:03am(T4)。

    至此,Device A已经拥有足够的信息来计算两个重要的参数:

    • NTP报文的往返时延Delay=(T4-T1)-(T3-T2)=2秒。
    • Device A相对Device B的时间差offset=((T2-T1)+(T3-T4))/2=1小时。

    NTP的报文格式

    NTP有两种不同类型的报文,一种是时钟同步报文,另一种是控制报文(仅用于需要网络管理的场合,与本文无关,这里不做介绍)。

    NTP基于UDP报文进行传输,使用的UDP端口号为123;时钟同步报文封装在UDP报文中,其格式如下图所示。

            

     

    主要字段的解释如下:

    • LI(Leap Indicator):长度为2比特,值为“11”时表示告警状态,时钟未被同步。为其他值时NTP本身不做处理。
    • VN(Version Number):长度为3比特,表示NTP的版本号,目前的最新版本为3。
    • Mode:长度为3比特,表示NTP的工作模式。不同的值所表示的含义分别是:0未定义、1表示主动对等体模式、2表示被动对等体模式、3表示客户模式、4表示服务器模式、5表示广播模式或组播模式、6表示此报文为NTP控制报文、7预留给内部使用。
    • Stratum:系统时钟的层数,取值范围为1~16,它定义了时钟的准确度。层数为1的时钟准确度最高,准确度从1到16依次递减,层数为16的时钟处于未同步状态,不能作为参考时钟。
    • Poll:轮询时间,即两个连续NTP报文之间的时间间隔。
    • Precision:系统时钟的精度。
    • Root Delay:本地到主参考时钟源的往返时间。
    • Root Dispersion:系统时钟相对于主参考时钟的最大误差。
    • Reference Identifier:参考时钟源的标识。
    • Reference Timestamp:系统时钟最后一次被设定或更新的时间。
    • Originate Timestamp:NTP请求报文离开发送端时发送端的本地时间。
    • Receive Timestamp:NTP请求报文到达接收端时接收端的本地时间。
    • Transmit Timestamp:应答报文离开应答者时应答者的本地时间。
    • Authenticator:验证信息。

    NTP时间同步的实现

    有了上述基础知识后,我们就可以实现自己的时间同步工具了,下文附了一个简单的C#的实现。

        class NptClient

        {

            IPAddress ntpServer;

            public NptClient(IPAddress ntpServer)

            {

                this.ntpServer = ntpServer;

            }

     

            public DateTime GetServerTime()

            {

                var startTime = DateTime.Now;

                var ntpTime = NTPData.Test(ntpServer);

                var recvTime = DateTime.Now;

     

                var offset = ((ntpTime.ReceiveTimestamp - startTime) + (ntpTime.TransmitTimestamp - recvTime));

                offset = offset.Subtract(TimeSpan.FromSeconds(offset.TotalSeconds / 2));

     

                return recvTime + offset;

            }

        }

     

        [StructLayout(LayoutKind.Sequential)]

        class NTPData

        {

            byte header = 0;

            byte Stratum = 1;           //系统时钟的层数,取值范围为1~16,它定义了时钟的准确度

            byte Poll = 1;              //轮询时间,即两个连续NTP报文之间的时间间隔

            byte Precision = 1;         //系统时钟的精度

            BigEndianUInt32 rootDelay;

            BigEndianUInt32 referenceIdentifier;

            BigEndianUInt32 ReferenceIdentifier;

     

            public NtpTime ReferenceTimestamp { get; private set; }

            public NtpTime OriginateTimestamp { get; private set; }

            public NtpTime ReceiveTimestamp { get; private set; }

            public NtpTime TransmitTimestamp { get; private set; }

     

            public NTPData()

            {

                this.header = GetHeader();

            }

     

            byte GetHeader()

            {

                var LI = "00";

                var VN = "011";         //NTP的版本号为3

                var Mode = "011";       //客户模式

     

                return Convert.ToByte(LI + VN + Mode, 2);

            }

     

            public static NTPData Test(IPAddress ntpServer)

            {

                var data = MarshalExtend.GetData(new NTPData());

     

                var udp = new System.Net.Sockets.UdpClient();

                udp.Send(data, data.Length, new IPEndPoint(ntpServer, 123));

     

                var ep = new IPEndPoint(IPAddress.Any, 0);

                var replyData = udp.Receive(ref ep);

     

                return MarshalExtend.GetStruct<NTPData>(replyData, replyData.Length);

            }

        }

     

        [StructLayout(LayoutKind.Sequential)]

        class NtpTime

        {

            BigEndianUInt32 seconds;

            BigEndianUInt32 fraction;

     

            static readonly DateTime baseTime = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc);

     

            public static implicit operator DateTime(NtpTime time)

            {

                /* rfc1305的ntp时间中,时间是用64bit来表示的,记录的是1900年后的秒数(utc格式)

                 * 高32位是整数部分,低32位是小数部分 */

     

                var milliseconds = (int)(((double)time.fraction / uint.MaxValue) * 1000);

                return baseTime.AddSeconds(time.seconds).AddMilliseconds(milliseconds).ToLocalTime();

            }

     

            public override string ToString()

            {

                return ((DateTime)this).ToString("o");

            }

        }

    当然,我这里只是在造重复轮子,网上是有不少功能完整的开源项目的。另外,如果对SNTPv4(RFC 2030)感兴趣的,可以参考一下这个页面上的实现——Simple Network Time (NTP) Protocol Client

    最后,附上几个可以使用(不保证,具体能用否还得看电信和方校长的心情)的NTP服务器:

    • 133.100.11.8 prefer
    • 210.72.145.44
    • 203.117.180.36
    • 131.107.1.10
    • time.asia.apple.com
    • 64.236.96.53
    • 130.149.17.21
    • 66.92.68.246
    • 18.145.0.30
    • clock.via.net
    • 137.92.140.80
    • 133.100.9.2
    • 128.118.46.3
    • ntp.nasa.gov
    • 129.7.1.66ntp-sop.inria.frserver
    • 210.72.145.44(中国国家授时中心服务器IP地址)
    • ntp.sjtu.edu.cn (上海交通大学网络中心NTP服务器地址)
    • 202.120.2.101 (上海交通大学网络中心NTP服务器地址)
  • 相关阅读:
    git+jenkins持续集成一:git上传代码
    性能测试五十:Jmeter+Influxdb+Grafana实时数据展示系统搭建
    性能测试四十九:ngrinder压测平台
    性能测试四十八:Jenkins+Ant+Jmeter系统部署
    性能测试四十七:jmeter性能监控工具ServerAgent
    工具篇:使用natapp工具映射内网到外网访问
    总想自己动动手系列·3·微信公众号和外网服务交互之通过TOKEN验证(准备篇·1)
    总想自己动动手系列·2·本地和外网(Liunx服务器上部署的web项目)按照自定义的报文格式进行交互(完结篇)
    总想自己动动手系列·1·本地和外网(Liunx服务器上部署的web项目)按照自定义的报文格式进行交互(准备篇)
    Linux-在新买的阿里云服务器上部署Tomcat并支持外网访问的配置(步骤记录)
  • 原文地址:https://www.cnblogs.com/MaxWoods/p/3296747.html
Copyright © 2011-2022 走看看