网络时间协议(
network time protocol,简称ntp)是用来在整个网络内发布精确时间的tcp/ip 协议,其本身的传输基于udp。其基本原理如下:
上图所示的是
ntp 基本工作原理,路由器gwa 和gwb 通过网络相连,它们都有自己独立的系统时钟, 要实现各自系统时钟的自动同步,作如下假设:
1 在
gwa 和gwb 的系统时钟同步之前, gwa 的时钟设定为10:00:00am,gwb 的时钟设定为11:00:00am。
2 以
gwb 为ntp 时间服务器,即gwa 将使自己的时钟与gwb 的时钟同步。
3 数据包在
gwa 和gwb 之间单向传输所需要的时间为1 秒。
系统时钟同步的工作过程如下:
1 gwa 发送一个
ntp 消息包给gwb,该消息包带有它离开gwa 时的时间戳,该时间戳为10:00:00am(t1)。
2 当此
ntp 消息包到达gwb 时,gwb 加上自己的时间戳,该时间戳为11:00:01am(t2)。
3 当此
ntp 消息包离开gwb 时,gwb 再加上自己的时间戳,该时间戳为11:00:02am(t3)。
4 当
gwa 接收到该响应消息包时,加上一个新的时间戳,该时间戳为10:00:03am(t4)。
至此,
gwa 已经拥有足够的信息,来计算两个重要的参数:
5 ntp 消息来回一个周期的时延
delay=(t4-t1)-(t3-t2)。
6 gwa 相对
gwb 的时间差offset=((t2-t1)+(t3-t4))/2。
这样,
gwa 就能够根据这些信息,来设定自己的时钟,使之与gwb 的时钟同步。这只是ntp 工作原理的一个粗略描述,在rfc1305 规范中,ntp 使用复杂的算法,来确保时钟同步的精确性。
根据网络结构以及路由器在网络中的位置,
ntp 有六种工作模式,其中前两种模式也称单播模式。
1 设置远程服务器为本地时间服务器,此时本地路由器工作在
client 模式。在这种工作模式下,只能是本地客户机同步到远程服务器,而远程服务器不会同步到本地客户机;
2 设置远程服务器作为本地路由器的对等体,本地运行在
symmetric active 模式(即主动模式)在。这种配置下,本地服务器能同步到远程服务器(被动模式),远程服务器也能同步到本地服务器。如果双方都有参考时钟,以层数小的为准;
3 设置本地路由器的一个接口发送
ntp 的广播消息包,此时,本地路由器工作在广播服务器模式;
4 设置本地路由器的一个接口接收
ntp 的广播信息包,此时,本地路由器工作在广播客户模式;
5 设置本地路由器的一个接口发送
ntp 组播消息包,本地路由器运行在组播服务器模式;
6 设置本地路由器的一个接口接收ntp 组播消息包,本地路由器运行在组播客户模式。
Linux下通过NTP协议实现服务器时间同步(C实现)
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#define
int8 char
#define uint8 unsigned char
#define uint32 unsigned int
#define ulong32 unsigned long
#define long32 long
#define int32 int
#define long64 long long
#define debug
//3600s*24h*(365days*70years+17days)
#define From00to70 0x83aa7e80U
#define NTPSVR1 "132.163.4.102" //USA
#define NTPSVR2 "132.163.135.132" //USA
#define NTPSVR3 "192.53.103.103" //Germany
#define NTPPORT 123
typedef struct NTPPACKET
{
uint8 li_vn_mode;
uint8 stratum;
uint8 poll;
uint8 precision;
ulong32 root_delay;
ulong32 root_dispersion;
int8 ref_id[4];
ulong32 reftimestamphigh;
ulong32 reftimestamplow;
ulong32 oritimestamphigh;
ulong32 oritimestamplow;
ulong32 recvtimestamphigh;
ulong32 recvtimestamplow;
ulong32 trantimestamphigh;
ulong32 trantimestamplow;
}NTPPacket;
NTPPacket ntppack,newpack;
//定义为long64,解决32位数的符号位问题
long64 firsttimestamp,finaltimestamp;
long64 diftime,delaytime;
void NTP_Init()
{
bzero(&ntppack,sizeof(ntppack));
ntppack.li_vn_mode=0x1b;//0|(3<<2)|(3<<5);
//获取初始时间戳T1
firsttimestamp="From00to70"+time(NULL);//-8*3600;
ntppack.oritimestamphigh=htonl(firsttimestamp);
}
int main()
{
// ulong32 clienttime;
// ulong32 diftime,firsttimestamp,finaltimestamp;
fd_set inset1;
int32 sockfd;
struct tim tv,tv1;
struct timezone tz;
struct sockaddr_in addr;
// printf("%d,%d,%d/n",&ntppack.li_vn_mode,&ntppack.stratum,&ntppack.poll);
// printf("%d %ld/n",sizeof(NTPPacket),sizeof(ntppack));
// printf("%ld/n",time(NULL));
if((sockfd=socket(AF_INET,SOCK_DGRAM,0))<0)
{
perror("create socket error!/n");
exit(1);
}
addr.sin_family=AF_INET; //IPV4协议
addr.sin_port =htons(NTPPORT); //NTP专用的123端口
addr.sin_addr.s_addr=inet_addr(NTPSVR1); //校时服务器
bzero(&(addr.sin_zero),8); //清零
//wait 5s
tv.tv_sec=10; //select等待时间为10S
tv.tv_usec=0;
FD_ZERO(&inset1);
FD_SET(sockfd,&inset1);
NTP_Init();
//发送数据请求包
sendto(sockfd,&ntppack,sizeof(ntppack),0,(struct sockaddr *)&addr,sizeof(struct sockaddr));
//select巡视
if(select(sockfd+1,&inset1,NULL,NULL,&tv)<0)
{
perror("select error!/n");
exit(1);
}
else
{
//printf("OK/n");
if(FD_ISSET(sockfd,&inset1))
{
// printf("OK/n");
if(recv(sockfd,&newpack,sizeof(newpack),0)<0) //接收数据在newpack中。
{
perror("recv error!/n");
exit(1);
}
}
}
//到达客户机时间戳T4
finaltimestamp=time(NULL)+From00to70;//-8*3600;
//将网络上传送的大端数据改为小端形式。
newpack.root_delay= ntohl(newpack.root_delay);
newpack.root_dispersion= ntohl(newpack.root_dispersion);
newpack.reftimestamphigh=ntohl(newpack.reftimestamphigh);
newpack.reftimestamplow= ntohl(newpack.reftimestamplow);
newpack.oritimestamphigh= ntohl(newpack.oritimestamphigh);
newpack.oritimestamplow= ntohl(newpack.oritimestamplow);
newpack.recvtimestamphigh= ntohl(newpack.recvtimestamphigh);
newpack.recvtimestamplow= ntohl(newpack.recvtimestamplow);
newpack.trantimestamphigh= ntohl(newpack.trantimestamphigh);
newpack.trantimestamplow= ntohl(newpack.trantimestamplow);
//求出客户机跟服务器的时间差=((T2-T1)+(T3-T4))/2
diftime=((newpack.recvtimestamphigh-firsttimestamp)+(newpack.trantimestamphigh-finaltimestamp))>>1;
//求出延时
delaytime=((newpack.recvtimestamphigh-firsttimestamp)-(newpack.trantimestamphigh-finaltimestamp))>>1;
//diftime=(5-9)>>1;
//求出真正时间的时间戳
tv1.tv_sec=time(NULL)+diftime+delaytime;
tv1.tv_usec=0;
//tz.
#ifdef debug
printf("/n/ndebug information .../n/n");
printf("time(NULL) is %ld/n",time(NULL));
printf("different time is %ld/n",diftime);
printf("delaytime is %ld/n",delaytime);
printf("time(NULL)+diftime+delaytime=%ld/n",time(NULL)+diftime+delaytime);
printf("tv1.tv_sec is %ld/n/n", tv1.tv_sec);
#endif
settimeofday(&tv1,NULL);
//diftime=diftime-From00to70;
#ifdef debug
//printf("different time is %ld/n",diftime);
printf("delay time is %ld/n",delaytime);
//printf("firsttimestamp is %x/n",time(NULL));
printf("newpack.tran is %ld/n",newpack.trantimestamphigh);
printf("newpack.recv is %ld/n",newpack.recvtimestamphigh);
printf("firsttimestamp is %ld/n",firsttimestamp);
printf("finaltimestamp is %ld/n",finaltimestamp);
printf("newpack.recv-firsttimestamp is %ld/n",newpack.recvtimestamphigh-firsttimestamp);
printf("newpack.tran-finaltimestamp is %ld/n",newpack.trantimestamphigh-finaltimestamp);
printf("(recv-first)+(ftran-final) is %ld/n",(newpack.recvtimestamphigh-firsttimestamp)+(newpack.trantimestamphigh-finaltimestamp));
printf("((recv-first)+(ftran-final))>>1 is %ld/n",((newpack.recvtimestamphigh-firsttimestamp)+(newpack.trantimestamphigh-finaltimestamp))>>1);
printf("different time is %ld/n/n",diftime);
printf("sizeof(long long) is:%d/n",sizeof(long long));
printf("Current time is.../n");
system("date");
#endif
}
#define uint8 unsigned char
#define uint32 unsigned int
#define ulong32 unsigned long
#define long32 long
#define int32 int
#define long64 long long
#define debug
//3600s*24h*(365days*70years+17days)
#define From00to70 0x83aa7e80U
#define NTPSVR1 "132.163.4.102" //USA
#define NTPSVR2 "132.163.135.132" //USA
#define NTPSVR3 "192.53.103.103" //Germany
#define NTPPORT 123
typedef struct NTPPACKET
{
uint8 li_vn_mode;
uint8 stratum;
uint8 poll;
uint8 precision;
ulong32 root_delay;
ulong32 root_dispersion;
int8 ref_id[4];
ulong32 reftimestamphigh;
ulong32 reftimestamplow;
ulong32 oritimestamphigh;
ulong32 oritimestamplow;
ulong32 recvtimestamphigh;
ulong32 recvtimestamplow;
ulong32 trantimestamphigh;
ulong32 trantimestamplow;
}NTPPacket;
NTPPacket ntppack,newpack;
//定义为long64,解决32位数的符号位问题
long64 firsttimestamp,finaltimestamp;
long64 diftime,delaytime;
void NTP_Init()
{
bzero(&ntppack,sizeof(ntppack));
ntppack.li_vn_mode=0x1b;//0|(3<<2)|(3<<5);
//获取初始时间戳T1
firsttimestamp="From00to70"+time(NULL);//-8*3600;
ntppack.oritimestamphigh=htonl(firsttimestamp);
}
int main()
{
// ulong32 clienttime;
// ulong32 diftime,firsttimestamp,finaltimestamp;
fd_set inset1;
int32 sockfd;
struct tim tv,tv1;
struct timezone tz;
struct sockaddr_in addr;
// printf("%d,%d,%d/n",&ntppack.li_vn_mode,&ntppack.stratum,&ntppack.poll);
// printf("%d %ld/n",sizeof(NTPPacket),sizeof(ntppack));
// printf("%ld/n",time(NULL));
if((sockfd=socket(AF_INET,SOCK_DGRAM,0))<0)
{
perror("create socket error!/n");
exit(1);
}
addr.sin_family=AF_INET; //IPV4协议
addr.sin_port =htons(NTPPORT); //NTP专用的123端口
addr.sin_addr.s_addr=inet_addr(NTPSVR1); //校时服务器
bzero(&(addr.sin_zero),8); //清零
//wait 5s
tv.tv_sec=10; //select等待时间为10S
tv.tv_usec=0;
FD_ZERO(&inset1);
FD_SET(sockfd,&inset1);
NTP_Init();
//发送数据请求包
sendto(sockfd,&ntppack,sizeof(ntppack),0,(struct sockaddr *)&addr,sizeof(struct sockaddr));
//select巡视
if(select(sockfd+1,&inset1,NULL,NULL,&tv)<0)
{
perror("select error!/n");
exit(1);
}
else
{
//printf("OK/n");
if(FD_ISSET(sockfd,&inset1))
{
// printf("OK/n");
if(recv(sockfd,&newpack,sizeof(newpack),0)<0) //接收数据在newpack中。
{
perror("recv error!/n");
exit(1);
}
}
}
//到达客户机时间戳T4
finaltimestamp=time(NULL)+From00to70;//-8*3600;
//将网络上传送的大端数据改为小端形式。
newpack.root_delay= ntohl(newpack.root_delay);
newpack.root_dispersion= ntohl(newpack.root_dispersion);
newpack.reftimestamphigh=ntohl(newpack.reftimestamphigh);
newpack.reftimestamplow= ntohl(newpack.reftimestamplow);
newpack.oritimestamphigh= ntohl(newpack.oritimestamphigh);
newpack.oritimestamplow= ntohl(newpack.oritimestamplow);
newpack.recvtimestamphigh= ntohl(newpack.recvtimestamphigh);
newpack.recvtimestamplow= ntohl(newpack.recvtimestamplow);
newpack.trantimestamphigh= ntohl(newpack.trantimestamphigh);
newpack.trantimestamplow= ntohl(newpack.trantimestamplow);
//求出客户机跟服务器的时间差=((T2-T1)+(T3-T4))/2
diftime=((newpack.recvtimestamphigh-firsttimestamp)+(newpack.trantimestamphigh-finaltimestamp))>>1;
//求出延时
delaytime=((newpack.recvtimestamphigh-firsttimestamp)-(newpack.trantimestamphigh-finaltimestamp))>>1;
//diftime=(5-9)>>1;
//求出真正时间的时间戳
tv1.tv_sec=time(NULL)+diftime+delaytime;
tv1.tv_usec=0;
//tz.
#ifdef debug
printf("/n/ndebug information .../n/n");
printf("time(NULL) is %ld/n",time(NULL));
printf("different time is %ld/n",diftime);
printf("delaytime is %ld/n",delaytime);
printf("time(NULL)+diftime+delaytime=%ld/n",time(NULL)+diftime+delaytime);
printf("tv1.tv_sec is %ld/n/n", tv1.tv_sec);
#endif
settimeofday(&tv1,NULL);
//diftime=diftime-From00to70;
#ifdef debug
//printf("different time is %ld/n",diftime);
printf("delay time is %ld/n",delaytime);
//printf("firsttimestamp is %x/n",time(NULL));
printf("newpack.tran is %ld/n",newpack.trantimestamphigh);
printf("newpack.recv is %ld/n",newpack.recvtimestamphigh);
printf("firsttimestamp is %ld/n",firsttimestamp);
printf("finaltimestamp is %ld/n",finaltimestamp);
printf("newpack.recv-firsttimestamp is %ld/n",newpack.recvtimestamphigh-firsttimestamp);
printf("newpack.tran-finaltimestamp is %ld/n",newpack.trantimestamphigh-finaltimestamp);
printf("(recv-first)+(ftran-final) is %ld/n",(newpack.recvtimestamphigh-firsttimestamp)+(newpack.trantimestamphigh-finaltimestamp));
printf("((recv-first)+(ftran-final))>>1 is %ld/n",((newpack.recvtimestamphigh-firsttimestamp)+(newpack.trantimestamphigh-finaltimestamp))>>1);
printf("different time is %ld/n/n",diftime);
printf("sizeof(long long) is:%d/n",sizeof(long long));
printf("Current time is.../n");
system("date");
#endif
}