简单的NTP客户端实现
2016-03-29 13:42
381 查看
NTP(Network Time Protocol,网络时间协议)是由RFC 1305定义的时间同步协议,用来在分布式时间服务器和客户端之间进行时间同步。NTP基于UDP报文进行传输,使用的UDP端口号为123。
使用NTP的目的是对网络内所有具有时钟的设备进行时钟同步,使网络内所有设备的时钟保持一致,从而使设备能够提供基于统一时间的多种应用。
对于运行NTP的本地系统,既可以接收来自其他时钟源的同步,又可以作为时钟源同步其他的时钟,并且可以和其他设备互相同步。
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小时。
这样,Device A就能够根据这些信息来设定自己的时钟,使之与Device B的时钟同步。
NTP的报文格式
NTP有两种不同类型的报文,一种是时钟同步报文,另一种是控制报文。控制报文仅用于需要网络管理的场合,它对于时钟同步功能来说并不是必需的,这里不做介绍。
主要字段的解释如下:
l LI(Leap Indicator):长度为2比特,值为“11”时表示告警状态,时钟未被同步。为其他值时NTP本身不做处理。
l VN(Version Number):长度为3比特,表示NTP的版本号,目前的最新版本为3。
l Mode:长度为3比特,表示NTP的工作模式。不同的值所表示的含义分别是:0未定义、1表示主动对等体模式、2表示被动对等体模式、3表示客户模式、4表示服务器模式、5表示广播模式或组播模式、6表示此报文为NTP控制报文、7预留给内部使用。
l Stratum:系统时钟的层数,取值范围为1~16,它定义了时钟的准确度。层数为1的时钟准确度最高,准确度从1到16依次递减,层数为16的时钟处于未同步状态,不能作为参考时钟。
l Poll:轮询时间,即两个连续NTP报文之间的时间间隔。
l Precision:系统时钟的精度。
l Root Delay:本地到主参考时钟源的往返时间。
l Root Dispersion:系统时钟相对于主参考时钟的最大误差。
l Reference Identifier:参考时钟源的标识。
l Reference Timestamp:系统时钟最后一次被设定或更新的时间。
l Originate Timestamp:NTP请求报文离开发送端时发送端的本地时间。
l Receive Timestamp:NTP请求报文到达接收端时接收端的本地时间。
l Transmit Timestamp:应答报文离开应答者时应答者的本地时间。
l Authenticator:验证信息。
NTP的工作模式
设备可以采用多种NTP工作模式进行时间同步:
客户端/服务器模式
对等体模式
广播模式
组播模式
用户可以根据需要选择合适的工作模式。在不能确定服务器或对等体IP地址、网络中需要同步的设备很多等情况下,可以通过广播或组播模式实现时钟同步;客户端/服务器和对等体模式中,设备从指定的服务器或对等体获得时钟同步,增加了时钟的可靠性。
以上信息来自于http://blog.163.com/yzc_5001/blog/static/2061963420121283050787/
下面是NTP客户端实现代码:
使用NTP的目的是对网络内所有具有时钟的设备进行时钟同步,使网络内所有设备的时钟保持一致,从而使设备能够提供基于统一时间的多种应用。
对于运行NTP的本地系统,既可以接收来自其他时钟源的同步,又可以作为时钟源同步其他的时钟,并且可以和其他设备互相同步。
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小时。
这样,Device A就能够根据这些信息来设定自己的时钟,使之与Device B的时钟同步。
NTP的报文格式
NTP有两种不同类型的报文,一种是时钟同步报文,另一种是控制报文。控制报文仅用于需要网络管理的场合,它对于时钟同步功能来说并不是必需的,这里不做介绍。
主要字段的解释如下:
l LI(Leap Indicator):长度为2比特,值为“11”时表示告警状态,时钟未被同步。为其他值时NTP本身不做处理。
l VN(Version Number):长度为3比特,表示NTP的版本号,目前的最新版本为3。
l Mode:长度为3比特,表示NTP的工作模式。不同的值所表示的含义分别是:0未定义、1表示主动对等体模式、2表示被动对等体模式、3表示客户模式、4表示服务器模式、5表示广播模式或组播模式、6表示此报文为NTP控制报文、7预留给内部使用。
l Stratum:系统时钟的层数,取值范围为1~16,它定义了时钟的准确度。层数为1的时钟准确度最高,准确度从1到16依次递减,层数为16的时钟处于未同步状态,不能作为参考时钟。
l Poll:轮询时间,即两个连续NTP报文之间的时间间隔。
l Precision:系统时钟的精度。
l Root Delay:本地到主参考时钟源的往返时间。
l Root Dispersion:系统时钟相对于主参考时钟的最大误差。
l Reference Identifier:参考时钟源的标识。
l Reference Timestamp:系统时钟最后一次被设定或更新的时间。
l Originate Timestamp:NTP请求报文离开发送端时发送端的本地时间。
l Receive Timestamp:NTP请求报文到达接收端时接收端的本地时间。
l Transmit Timestamp:应答报文离开应答者时应答者的本地时间。
l Authenticator:验证信息。
NTP的工作模式
设备可以采用多种NTP工作模式进行时间同步:
客户端/服务器模式
对等体模式
广播模式
组播模式
用户可以根据需要选择合适的工作模式。在不能确定服务器或对等体IP地址、网络中需要同步的设备很多等情况下,可以通过广播或组播模式实现时钟同步;客户端/服务器和对等体模式中,设备从指定的服务器或对等体获得时钟同步,增加了时钟的可靠性。
以上信息来自于http://blog.163.com/yzc_5001/blog/static/2061963420121283050787/
下面是NTP客户端实现代码:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <netdb.h> #include <sys/socket.h> #include <sys/time.h> #include <netinet/in.h> #include <arpa/inet.h> #define SUB70 0x83aa7e80U //1900-1970之间的时间差 #define NTP_PORT 123 #define NTP_SERVER "120.24.166.46" #define uint32 unsigned int #define uint64 unsigned long long #define LI 0 #define VN 3 #define MODE 3 #define STRATUM 0 #define POLL 4 #define PREC -6 typedef struct _ntppack { uint32 li_vn; //LI VN MODE STRATUM POLL PRECISION uint32 root_delay; //本地到主参考时钟源的往返时间 uint32 root_dispersion; //系统时钟相对于主参考时钟的最大误差 uint32 reference_identifier; //参考时钟源标识 uint64 reference_time; //T4 系统时间最后一次被改变 uint64 originate_time; //T1 离开发送端时发送端的时间 uint64 receive_time; //T2 到达服务器时服务器的时间 uint64 transmit_time; //T3 离开服务器时服务器的时间 }NTP; NTP send_pack,recv_pack; uint64 firsttime,finaltime; uint64 delaytime,diftime; void init(void) { send_pack.li_vn = htonl((LI << 30)|(VN << 27)|(MODE << 24)|(STRATUM << 16)|(POLL << 8)|(PREC & 0xff)); firsttime = (long long)time(NULL)+SUB70; send_pack.reference_time = htonl(firsttime); } int main(void) { int sock; struct sockaddr_in addr; struct timeval tv1; if((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { perror("socket"); exit(1); } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(NTP_PORT); addr.sin_addr.s_addr = inet_addr(NTP_SERVER); init(); sendto(sock, &send_pack, sizeof(send_pack), 0, (struct sockaddr *)&addr, sizeof(struct sockaddr)); recv(sock, &recv_pack, sizeof(recv_pack), 0);<span style="white-space:pre"> </span>//block /****转换字节序****/ recv_pack.root_delay = ntohl(recv_pack.root_delay); recv_pack.root_dispersion = ntohl(recv_pack.root_dispersion); recv_pack.reference_identifier = ntohl(recv_pack.reference_identifier); recv_pack.reference_time = ntohl(recv_pack.reference_time); recv_pack.originate_time = ntohl(recv_pack.originate_time); recv_pack.receive_time = ntohl(recv_pack.receive_time); recv_pack.transmit_time = ntohl(recv_pack.transmit_time); diftime = ((recv_pack.receive_time - firsttime) +(recv_pack.transmit_time - finaltime)) >> 1; delaytime = ((finaltime - firsttime) - (recv_pack.transmit_time - recv_pack.receive_time)) >> 1; tv1.tv_sec = time(NULL)+diftime+delaytime; tv1.tv_usec = 0; if(settimeofday(&tv1, NULL) < 0) { perror("settimeofday"); exit(1); } system("date"); return 0; }
相关文章推荐
- PHP JSON中文乱码解决方法大全
- ntpd使用
- php文件加密
- getAttribute()与getParameter()
- PsySH——PHP交互式控制台
- PHP-点滴记录
- PHP实现163邮箱自动发送邮件
- yii2表单验证方法
- php数组的各种排序
- php二位数组合并
- Laravel5.1数据库连接、创建数据库、创建model及创建控制器的方法
- php单例模式
- PHPCrawler抓取酷狗精选集歌单
- PHP创建word文档的方法(平台无关)
- [MarsZ]ThinkPHP项目实战总结
- Ubuntu上的ftp配置连接
- 开发自己的PHP MVC框架(二)
- PHP中key和current,next的联合运用实例分析
- static 静态方法 有什么优缺点 PHP中静态方法(static)与非静态方法的使用及区别
- [MarsZ]ThinkPHP项目实战总结