通过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服务器地址)
通过NTP协议进行时间同步的更多相关文章
- 使用NTP协议服务器时间同步
NTP是用来使系统和一个精确的时间源保持时间同步的协议.建议大家在自己管理的网络中建立至少一台时间服务器来同步本地时间,这样可以使得在不同的系统上处理和收集日志和管理更加容易.我们分别从windows ...
- golang ntp协议客户端
NTP(Network Time Protocol,网络时间协议)是由RFC 1305定义的时间同步协议,用来在分布式时间服务器和客户端之间进行时间同步.NTP基于UDP报文进行传输,使用的UDP端口 ...
- 部署NTP服务器进行时间同步
NTP服务端:linl_S IP:10.0.0.15 NTP客户端:lin_C IP:10.0.0.16 NTP服务概述 1.原理 NTP(Network TimeProtocol,网络时 ...
- 实现基于NTP协议的网络校时功能
无论PC端还是移动端系统都自带时间同步功能,基于的都是NTP协议,这里使用C#来实现基于NTP协议的网络校时功能(也就是实现时间同步). 1.NTP原理 NTP[Network Time Protoc ...
- NTP 协议介绍
NTP协议 NTP(Network Time Protocol,网络时间协议)是由RFC 1305定义的时间同步协议,用来在分布式时间服务器和客户端之间进行时间同步.NTP基于UDP报文进行传输,使用 ...
- ntp时间服务器 时间同步
具体两种模式 1.服务器数量比较少,可以直接与时间服务器同步 2.本地服务器较多,在本地自建时间同步服务器, 时间同步的两个命令 ntpd : 校准时间,一点点的校准过来时间的 ...
- linux服务之NTP及chrony时间同步
博客园 首页 联系 管理 linux服务之NTP及chrony时间同步 一.NTP时间同步 NTP(Network Time Protocol,网络时间协议)是由RFC 1305定义的时间同步 ...
- 使用ntp协议同步本地时间(C语言)
使用ntp协议同步本地时间 同步服务器使用的东北大学网络授时服务:ntp.neu.edu.cn更多ntp服务器 http://www.ntp.org.cn/ 源代码来自网络,经本人精简ntp部分,供大 ...
- CentOS 7.2部署NTP服务器实现时间同步
CentOS 7.2部署NTP服务器实现时间同步 [日期:2017-12-18] 来源:Linux社区 作者:梁明远 [字体:大 中 小] 1. 前言 对于容器编排系统,前段时间主要研究kube ...
随机推荐
- Ansible Tower系列 二(安装 Tower)【转】
文档:http://docs.ansible.com/ansible-tower/ 安装前检查 python版本为2.6 保持网络畅通 内存预留充足 安装用户为root 软件下载 下载地址:http: ...
- springcloud Zuul中异常处理细节
Spring Cloud Zuul对异常的处理整体来说还是比较方便的,流程也比较清晰,只是由于Spring Cloud发展较快,各个版本之间有差异,导致有的小伙伴在寻找这方面的资料的时候经常云里雾里, ...
- springcloud注解@EnableDiscoveryClient与@EnableEurekaC
spring cloud中discovery service有许多种实现(eureka.consul.zookeeper等等),@EnableDiscoveryClient基于spring-cloud ...
- 在VMware上安装Ubuntu软件步骤与遇到的相关问题及解决方案
图解演示环境版本: 本机系统: WIN10 虚拟机:VMware Workstation 12(中文版) 安装目标:Ubuntu Desktop 12.04 LTS (请点击这里)先下载好iso镜像 ...
- python2脚本批量转成python3
#查看 模块的安装路径 import tools print (tools.__file__) #进入cmd,然后python 2to3.py -w 需要批量转换的文件所在的路径#参考:http:// ...
- 详解PHP的执行原理和流程
简介 先看看下面这个过程: • 我们从未手动开启过PHP的相关进程,它是随着Apache的启动而运行的: • PHP通过mod_php5.so模块和Apache相连(具体说来是SAPI,即服务器应用程 ...
- java 代理 agency
java并没有对其提供直接的支持,这是继承和组合的中庸之道,因为我们将一个成员对象置于所要构造的类中(组合),但与此同时我们在新类中暴露了该成员的所有方法(就像继承),使用代理时可以拥有更多的控制力, ...
- 《Redis设计与实现》学习笔记
第2章 简单动态字符串(SDS) redis的字符串不是直接用c语言的字符串,而是用了一种称为简单动态字符串(SDS)的抽象类型,并将其作为默认字符串. redis中包含字符串值的键值对在底层都是由S ...
- mysql xtrabackup工具备份
一.注意事项 建议用xtrabackup备份时采用全备或增量备份的方式,楼主也尝试过单库备份,但是都以备份后恢复到已经存在数据库的mysqldata目录下后部分库会出幺蛾子而告终,建议使用mysqld ...
- [Python]conda与 virtualenv虚拟环境配置
参考: Anaconda使用总结 Anacodna之conda与 virtualenv对比使用教程,创建虚拟环境 conda设置Python虚拟环境 python的virtualenv环境与使用 有时 ...