通过NTP协议进行时间同步

摘要:
设备A和设备B通过网络连接。它们都有自己的独立系统时钟,需要通过NTP自动同步。DeviceB充当NTP时间服务器,即DeviceA将自己的时钟与DeviceB的时钟同步。设备A和设备B之间单向传输NTP消息所需的时间为1秒。NTP基于UDP消息传输,使用的UDP端口号为123;时钟同步消息封装在UDP消息中,其格式如下图所示。

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

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

NTP工作原理

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

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

        通过NTP协议进行时间同步第1张

 

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

至此,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报文中,其格式如下图所示。

        通过NTP协议进行时间同步第2张

主要字段的解释如下:

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

NTP时间同步的实现

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

    classNptClient

    {

        IPAddress ntpServer;

        public NptClient(IPAddress ntpServer)

        {

            this.ntpServer = ntpServer;

        }

 

        publicDateTime 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)]

    classNTPData

    {

        byte header = 0;

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

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

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

        BigEndianUInt32 rootDelay;

        BigEndianUInt32 referenceIdentifier;

        BigEndianUInt32 ReferenceIdentifier;

 

        publicNtpTime ReferenceTimestamp { get; privateset; }

        publicNtpTime OriginateTimestamp { get; privateset; }

        publicNtpTime ReceiveTimestamp { get; privateset; }

        publicNtpTime TransmitTimestamp { get; privateset; }

 

        public NTPData()

        {

            this.header = GetHeader();

        }

 

        byte GetHeader()

        {

            var LI = "00";

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

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

 

            returnConvert.ToByte(LI + VN + Mode, 2);

        }

 

        publicstaticNTPData Test(IPAddress ntpServer)

        {

            var data = MarshalExtend.GetData(newNTPData());

 

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

            udp.Send(data, data.Length, newIPEndPoint(ntpServer, 123));

 

            var ep = newIPEndPoint(IPAddress.Any, 0);

            var replyData = udp.Receive(ref ep);

 

            returnMarshalExtend.GetStruct<NTPData>(replyData, replyData.Length);

        }

    }

 

    [StructLayout(LayoutKind.Sequential)]

    classNtpTime

    {

        BigEndianUInt32 seconds;

        BigEndianUInt32 fraction;

 

        staticreadonlyDateTime baseTime = newDateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc);

 

        publicstaticimplicitoperatorDateTime(NtpTime time)

        {

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

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

 

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

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

        }

 

        publicoverridestring 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服务器地址)

免责声明:文章转载自《通过NTP协议进行时间同步》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇使用filebeat 收集日志到logstash 收集日志redis再到logstash到es电脑系统管理员权限怎么设置为超级管理员权限下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

数据仓库基础(六)数据的ETL

ETL是数据抽取(Extract)、转换(Transform)、加载(Load)的简写,是构建数据仓库最重要的一步。 1.抽取 抽取时元数据进入到数据仓库的第一步。因为每个业务系统数据的质量不相同,所以需要对每个数据源建立不同的抽取程序。 抽取的主要功能: 提供数据匹配器的功能:这样使得程序可以与多种业务数据源相连接。 提供标准化的功能:抽取最重要的一个功...

js new Date()参数格式

最近在写页面使用new Date()获取时间戳在ie浏览器中测试发现无效;后来发现是参数格式问题, new Date()参数格式如下: 1、用整数初始化日期对象var date1 = new Date(2017,06,06); console.log(date1); // Thu Jul 06 2017 00:00:00 GMT+0800 (中国标准时间)...

波场TRX 钱包开发,看这篇就够了

波场TRON作为一种基于区块链的去中心化内容协议,其目标意为通过区块链与分布式存储技术,构建一个全球范围内的自由内容娱乐体系,这个协议可以让每个用户自由发布,存储,拥有数据,并通过去中心化的自治形式,以数字资产发行,流通,交易方式决定内容的分发、订阅、推送,赋能内容创造者,形成去中心化的内容娱乐生态。   TRX是TRON区块链上账户的基本单位,所有其他代...

Python:日期和时间的处理模块及相关函数

Python:日期和时间的处理模块及相关函数   Python 提供 time 模块和 calendar 模块用于格式化日期和时间。 一、时间戳 在Python中,时间戳是以秒为单位的浮点小数,它是指格林威治时间自1970年1月1日(00:00:00 GMT)至当前时间的总秒数,也被称为Unix时间戳(Unix Timestamp)。时间戳唯一地标识某一刻...

dubbo心跳机制 (2)

此文已由作者赵计刚授权网易云社区发布。 欢迎访问网易云社区,了解更多网易技术产品运营经验。 来看一下HeaderExchangeServer.this.getChannels(): 1publicCollection<Channel>getChannels(){ 2return(Collection)getExchangeChannels()...

python模块学习心得

初始模块 1.什么是模块 模块是用来实现某项功能的一大堆代码,为什么会有模块呢?过程式编程的时候为了减少程序员编程代码的重复性,就利用函数的调用减少了代码的重复性,但是某些时候程序会过于的庞大,我们会用到很多很多 的函数,同样是为了方便,我们就把某些函数在一起共同产生的一些功能放在同一个py文件里面,这个py文件就称为一个模块,或者多个py文件在一个文件夹...