作用
大数据产生与处理系统是各种计算设备集群的,计算设备将统一、同步的标准时间用于记录各种事件发生时序,如 E-MAIL 信息、文件创建和访问时间、数据库处理时间等。大数据系统内不同计算设备之间控制、计算、处理、应用等数据或操作都具有时序性,若计算机时间不同步,这些应用或操作或将无法正常进行。大数据系统是对时间敏感的计算处理系统,时间同步是大数据能够得到正确处理的基础保障,是大数据得以发挥作用的技术支撑。大数据时代,整个处理计算系统内的大数据通信都是通过网络进行。时间同步也是如此,利用大数据的互联网络传送标准时间信息,实现大数据系统内时间同步。网络时间同步协议(NTP)是时间同步的技术基础。
NTP 是用来获取网络时间,它是一个 UDP 服务器,你给它发送一个正确格式的数据,他就会回复你
在 CentOS8.0 中默认不再支持 ntp 软件包,时间同步将由 chrony 来实现。chrony 是一种相对于 ntp 更好的时间同步程序。
全球一共有很多 NTP 服务器,例如我们去网上搜索 NTP 服务器就会看到:
NTP 服务器一般使用的端口号为 123
NTP 的协议格式:
NTP packet = NTP header(16 字节) + 4 个时间戳(每个 8 字节) = 48 字节其中 NTP header 的顺序格式:LI | VN |Mode | Stratum | Poll | Precision |Root Delay |Root Dispersion |Reference IdentifierLi - LeapYearIndicator : 2bitVN - VersionNumber : 3bitMode : 3 bitStratum : 8bitPoll - PollInterval : 8 bitPercision : 8bitRoot delay : 32bitRoot Dispersion : 32bitReference Identifier : 32bit主要字段的解释如下: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:验证信息。四个时间戳(TimeStamps) : 共 32 字节Reference Timestamp : 64bit 提出时间Originate Timestamp : 64bit 客户端发送请求的时间Receive Timestamp : 64bit 服务器接收请求的时间Transmit Timestamp : 64bit 服务器答复时间
知道上面格式我们就可以通过 UDP 发送协议报文给 NTP 服务器获取时间信息
实例程序
#include<stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
//秒---》时间字符串
//ctime 1970 1 1 到现在的秒数
//网络时间 1900 1 1
#define NTP_SEC 2208988800ull // 1900~1970 年时间差
int main(int argc,char *argv[])
{
//创建一个套接字
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if(sock == -1)
{
perror("socket error");
return -1;
}
//2.初始化一个目的地的 IPV4 结构体,你的服务器的地址
// prot:123 阿里云 NTP 服务器:182.92.12.11
struct sockaddr_in sAddr;
memset(&sAddr,0,sizeof(sAddr));//内存设置
sAddr.sin_family = AF_INET; //指定协议族 AF_INET
sAddr.sin_port = htons(atoi(argv[1])); //端口号
//sAddr.sin_addr; //ip 地址
//inet_aton(argv[2],&(sAddr.sin_addr));
sAddr.sin_addr.s_addr = inet_addr(argv[2]);
unsigned char buf[48] = {0};
buf[0] = 0x1b;
//发送消息
int res = sendto(sock,buf, 48, 0,(struct sockaddr*)&sAddr,sizeof(sAddr));
struct sockaddr_in sa;
socklen_t addrlen = sizeof(sa);
res = recvfrom(sock,buf,48,0,(struct sockaddr *)&sa,&addrlen);
if(res >0)
{
printf("res = %d\n",res);
printf("server IP:%s
prot:%d\n",inet_ntoa(sa.sin_addr),ntohs(sa.sin_port));
//printf("recv message:%s\n",buf1);
unsigned int rec_sec = *(int *)(buf+40);//网络字节序
rec_sec = ntohl(rec_sec);
time_t cur_sec = rec_sec-NTP_SEC;//1970 到现在
printf("%s\n",ctime(&cur_sec));
}
close(sock);
return 0;
}