NTP协议详解_ntp是安全协议吗

NTP协议详解_ntp是安全协议吗前言NTP(NetworkTimeProtocol)网络时间协议基于UDP,用于网络时间同步的协议,使网络中的计算机时钟同步到UTC,再配合各个时区的偏移调整就能实现精准同步对时功能。提供NTP对时的服务器有很多,比如微软的NTP对时服务器,利用NTP服务器提供的对时功能,可以使我们的设备时钟系统能够正确运行。NTP报文格式NTP报文格式如上图所示,它的字段含义参考如下:L…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

前言

NTP(Network Time Protocol)网络时间协议基于UDP,用于网络时间同步的协议,使网络中的计算机时钟同步到UTC,再配合各个时区的偏移调整就能实现精准同步对时功能。提供NTP对时的服务器有很多,比如微软的NTP对时服务器,利用NTP服务器提供的对时功能,可以使我们的设备时钟系统能够正确运行。

NTP报文格式

NTP协议详解_ntp是安全协议吗

NTP报文格式如上图所示,它的字段含义参考如下:

  1. LI 闰秒标识器,占用2个bit
  2. VN 版本号,占用3个bits,表示NTP的版本号,现在为3
  3. Mode 模式,占用3个bits,表示模式
  4. stratum(层),占用8个bits
  5. Poll 测试间隔,占用8个bits,表示连续信息之间的最大间隔
  6. Precision 精度,占用8个bits,,表示本地时钟精度
  7. Root Delay根时延,占用8个bits,表示在主参考源之间往返的总共时延
  8. Root Dispersion根离散,占用8个bits,表示在主参考源有关的名义错误
  9. Reference Identifier参考时钟标识符,占用8个bits,用来标识特殊的参考源    
  10. 参考时间戳,64bits时间戳,本地时钟被修改的最新时间。
  11. 原始时间戳,客户端发送的时间,64bits。
  12. 接受时间戳,服务端接受到的时间,64bits。
  13. 传送时间戳,服务端送出应答的时间,64bits。
  14. 认证符(可选项)

NTP协议详解_ntp是安全协议吗

抛开复杂的协议报文,我们来理解一下NTP客户端与服务器的交互过程,进而理解参考时间戳、原始时间戳、接受时间戳、传送时间戳的关系。如图,客户端和服务端都有一个时间轴,分别代表着各自系统的时间,当客户端想要同步服务端的时间时,客户端会构造一个NTP协议包发送到NTP服务端,客户端会记下此时发送的时间t0,经过一段网络延时传输后,服务器在t1时刻收到数据包,经过一段时间处理后在t2时刻向客户端返回数据包,再经过一段网络延时传输后客户端在t3时刻收到NTP服务器数据包。特别声明,t0和t3是客户端时间系统的时间、t1和t2是NTP服务端时间系统的时间,它们是有区别的。对于时间要求不那么精准设备,直接使用NTP服务器返回t2时间也没有太大影响。但是作为一个标准的通信协议,它是精益求精且容不得过多误差的,于是必须计算上网络的传输延时。客户端与服务端的时间系统的偏移定义为θ、网络的往返延迟定义为δ,基于此,可以对t2进行精确的修正,已达到相关精度要求,它们的计算公式如下:

NTP协议详解_ntp是安全协议吗

式中:

t0是请求数据包传输的客户端时间戳

t1是请求数据包回复的服务器时间戳

t2是响应数据包传输的服务器时间戳

t3是响应数据包回复的客户端时间戳

对此,我们只需将NTP服务端返回的时间t2加上网络延时δ的一半就可以了(t2+δ/2)。

NTP请求样例

#include <sys/types.h>  
#include <sys/stat.h> 
#include <sys/ioctl.h> 
#include <sys/socket.h>
#include <sys/wait.h> 
#include <sys/time.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h> 
#include <stdio.h>  
#include <stdlib.h>
#include <string.h>  
#include <pthread.h>   
#include <dirent.h> 
#include <time.h>
#include <fcntl.h> 
#include <errno.h>

#define debugprintf 1
#ifdef debugprintf
	#define debugpri(mesg, args...) fprintf(stderr, "[NetRate print:%s:%d:] " mesg "\n", __FILE__, __LINE__, ##args) 
#else
	#define debugpri(mesg, args...)
#endif

#define JAN_1970     		0x83aa7e80
#define NTPFRAC(x) (4294 * (x) + ((1981 * (x))>>11))
#define USEC(x) (((x) >> 12) - 759 * ((((x) >> 10) + 32768) >> 16))
#define Data(i) ntohl(((unsigned int *)data)[i])
#define LI 0
#define VN 3
#define MODE 3
#define STRATUM 0
#define POLL 4 
#define PREC -6
struct NtpTime 
{
	unsigned int coarse;
	unsigned int fine;
};

void sendPacket(int fd)
{
	unsigned int data[12];
	struct timeval now;

	if (sizeof(data) != 48) 
	{
    	fprintf(stderr,"size error\n");
    	return;
	}

	memset((char*)data, 0, sizeof(data));
	data[0] = htonl((LI << 30) | (VN << 27) | (MODE << 24) | (STRATUM << 16) | (POLL << 8) | (PREC & 0xff));//构造协议头部信息
	data[1] = htonl(1<<16);
	data[2] = htonl(1<<16);
	gettimeofday(&now, NULL);
	data[10] = htonl(now.tv_sec + JAN_1970);//构造传输时间戳
	data[11] = htonl(NTPFRAC(now.tv_usec));
	send(fd, data, 48, 0);
}
//获取NTP服务器返回的时间
void getNewTime(unsigned int *data,struct timeval *ptimeval)
{
	struct NtpTime trantime;
	trantime.coarse = Data(10);
	trantime.fine   = Data(11);
	
	ptimeval->tv_sec 	= trantime.coarse - JAN_1970;
	ptimeval->tv_usec 	= USEC(trantime.fine);
}

int getNtpTime(struct hostent* phost,struct timeval *ptimeval)
{
	if(phost == NULL)
	{
		debugpri("err:host is null!\n");
		return -1;
	}
	int sockfd;
	struct sockaddr_in addr_src,addr_dst;
	fd_set fds;
	int ret;
	int recv_len;
	unsigned int buf[12];
	memset(buf,0,sizeof(buf));
	int addr_len;
	int count = 0;
	
	struct timeval timeout;

	addr_len = sizeof(struct sockaddr_in);

	memset(&addr_src, 0, addr_len);
	addr_src.sin_family = AF_INET;
	addr_src.sin_addr.s_addr = htonl(INADDR_ANY);
	addr_src.sin_port = htons(0);

	memset(&addr_dst, 0, addr_len);
	addr_dst.sin_family = AF_INET;
	memcpy(&(addr_dst.sin_addr.s_addr), phost->h_addr_list[0], 4);
	addr_dst.sin_port = htons(123);//ntp默认端口123

	if(-1==(sockfd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP)))//创建UDP socket
	{		
		debugpri("create socket error!\n");
		return -1;
	}

	ret = bind(sockfd, (struct sockaddr*)&addr_src, addr_len);//bind
	if(-1==ret)	
	{		
		debugpri("bind error!\n");		
		close(sockfd);		
		return -1;
	}
	
	ret = connect(sockfd, (struct sockaddr*)&addr_dst, addr_len);//连接NTP服务器
	if(-1==ret)	
	{		
		debugpri("connect error!\n");		
		close(sockfd);		
		return -1;
	}
	sendPacket(sockfd);	//发送请求包
	while (count < 50)//轮询请求
	{
		FD_ZERO(&fds);
		FD_SET(sockfd, &fds);

		timeout.tv_sec = 0;
		timeout.tv_usec = 100000;
		ret = select(sockfd + 1, &fds, NULL, NULL, &timeout);
		if (0 == ret)
		{
			count++;
			debugpri("ret == 0\n");
			sendPacket(sockfd);
			usleep(100*1000);
			continue;
		}
		if(FD_ISSET(sockfd, &fds))
		{
			recv_len = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&addr_dst, (socklen_t*)&addr_len);
			if(-1==recv_len)		
			{			
				debugpri("recvfrom error\n");			
				close(sockfd);			
				return -1;
			}
			else if(recv_len > 0)
			{
				debugpri("receiv data\n");
				getNewTime(buf,ptimeval);
				debugpri("sec = %d usec = %d",ptimeval->tv_sec ,ptimeval->tv_usec);//打印输出NTP服务器返回的时间
				break;
			}
		}
		else
		{
			debugpri("count %d \n",count);
			usleep(50*1000);
			count ++;
		}
	}
	if(count >=50)
	{
		debugpri("getNewTime   timeout fail \n");
		close(sockfd);
		return -1;
	}
	close(sockfd);
	return 0;
}

int main(int argc, char** argv)  
{
	struct timeval TimeSet;
	static struct hostent *host = NULL;
	
	host = gethostbyname(argv[1]);
	memset(&TimeSet ,0 ,sizeof(TimeSet));
	getNtpTime(host,&TimeSet);
	return 0;
	 
 }

常用的NTP服务端站点

time.windows.com

time.nist.gov

s1a.time.edu.cn

s1c.time.edu.cn

time-nw.nist.gov

time-a.nist.gov

time-b.nist.gov

s1b.time.edu.cn

nist1.aol-ca.truetime.com

总结:

NTP协议作为常用的通信协议,各种参考资料齐全,本文做一次梳理,以加深对NTP协议的理解。参考:

https://zh.m.wikipedia.org/wiki/%E7%B6%B2%E8%B7%AF%E6%99%82%E9%96%93%E5%8D%94%E5%AE%9A

https://wenku.baidu.com/view/4ab65c3ec850ad02de80418e.html

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/182662.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 修改ntp服务器地址,修改ntp服务器地址

    修改ntp服务器地址,修改ntp服务器地址修改ntp服务器地址内容精选换一换修改子网名称、DNS服务器地址等。当前在部分区域中,子网已从虚拟私有云中解耦,解耦后子网拥有独立入口。未解耦:在虚拟私有云详情页的“子网”页签,可对子网进行操作。本小节的操作步骤指导以此入口为例。已解耦:在进入“网络>虚拟私有云”后,在左侧导航栏直接选择“子网”,可对子网进行操作。登录管理控制台。在管理控制台左上角单击,选择区域和项目网关创建成功以后,…

    2022年6月14日
    30
  • 什么是实例化,实例化、声明、初始化的区别

    什么是实例化,实例化、声明、初始化的区别实例化 instantiate 是指在面向对象的编程中 把用类创建对象的过程称为实例化 是将一个抽象的概念类 具体到该类实物的过程 实例化过程中一般由类名对象名 new 类名 参数 1 参数 2 参数 n 构成 简介在面向对象的编程中 通常把用类创建对象的过程称为实例化 其格式如下 如 Datedate newDate 就是用日期类创建了一个日期的对象 就叫对象的实例化

    2025年10月27日
    4
  • 44页智慧生活社区+智慧小区建设方案[通俗易懂]

    44页智慧生活社区+智慧小区建设方案[通俗易懂]喜欢文章可以【转发➕评论】,关注公众号“智慧方案文库“,私信获取解决方案。本文章引用的资料均通过互联网等公开渠道合法获取,仅作为行业交流和学习使用,并无任何商业目的。其版权归原资料作者或出版社所有,作者不对所涉及的版权问题承担任何法律责任。若版权方、出版社认为本文章侵权,请立即通知作者删除。更多方案【2021】77页数字李生智慧园区解决方案(附下载)【2021】102页新一代数字化转型信息化总体规划方案(附下载)【2021】85页5G+物联网智慧校园解决方案(附下载)【2021】60页智慧城市运营管理平台

    2022年10月17日
    5
  • Matlab sqrt函数[通俗易懂]

    Matlab sqrt函数[通俗易懂]一、sqrt是计算平方根的函数,比如sqrt(4)=2等等。可以是一个数,也可以是一个矩阵。举例说明:>>sqrt(2)ans=1.4142>>sqrt(rand(4))ans=0.83020.47850.73370.32660.86500.95570.99810.98080.67120.390

    2022年6月8日
    105
  • session、cookie、token 详解

    发展史1、很久很久以前,Web基本上就是文档的浏览而已,既然是浏览,作为服务器,不需要记录谁在某一段时间里都浏览了什么文档,每次请求都是一个新的HTTP协议,就是请求加响应,尤其是我不用记住是谁刚刚发了HTTP请求,每个请求对我来说都是全新的。这段时间很嗨皮2、但是随着交互式Web应用的兴起,像在线购物网站,需要登录的网站等等,马上就面临一个问题,那就是要管理会话,…

    2022年4月6日
    54
  • 使用jedis操作redis_hadoop集群如何使用

    使用jedis操作redis_hadoop集群如何使用在上一篇文章中小编为大家介绍的Redis最新版本Redis-5.0.5版本的集群环境安装。集群环境的使用不可能使用客户端命令的方式,肯定要集成到我们的代码中以实现数据缓存功能。对应Java来说Redis官网向用户推荐了Java客户端,如下图所示:在Redis官网推荐的众多Java客户端中,Jedis是一个比较简单而功能强大的客户端,它实现Redis服务的封装,提供了简单的API调用。下面小编就…

    2025年10月10日
    5

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号