一、功能實現
以NTP服務器爲基準,同步網內計算機或嵌入式設備的時間。
二、協議原理
協議:
客戶端向NTP服務器發送請求數據包,NTP服務器迴應一個數據包。
分別記錄客戶端發送請求包時的時刻t1和接收到迴應包的時刻t4,服務器迴應的數據包內包含了服務器接收到請求包的時刻t2和服務器發送迴應包的時刻t3。
t4-t1表示整個消息傳遞過程所需要的時間;
t3-t2表示消息傳遞過程在服務器停留的時間;
(t4-t1)-(t3-t2)是來回路上消耗的時間,如果來回傳輸所用的時間一樣,那麼,單程的時間爲:
t = ( (t4 - t1) - (t3 - t2) )/2;
假定客戶端相對於服務器的時間誤差是dis,則有下列等式:
t2 = t1 + dis + t;
t4 = t3 - dis + t;
則:dis = (( t2 - t1 ) + ( t3 - t4 )) / 2;
根據此差值重新設置時間即可。
說明:
服務器發返回的時間是以1900年爲基準計算的,而linux是以1970年爲基準的,所以需要用到一個常數做轉化:
#define JAN_1970 2208988800UL /* 1970 - 1900 in seconds */
在計算與服務器的差值時會用到此值。
三、代碼實現
ntp.h
#ifndef __NTP_H
#define __NTP_H
#define JAN_1970 2208988800UL /* 1970 - 1900 in seconds */
#define NTP_SERVER_NAME "10.13.0.21" //my windows 7 ntp server
#define NTP_PORT 123
/* some ntp server
time-a.nist.gov
time-b.nist.gov
time-a.timefreq.bldrdoc.gov
time-b.timefreq.bldrdoc.gov
time-c.timefreq.bldrdoc.gov
utcnist.colorado.edu
ntp.tuxfamily.net
time-nw.nist.gov
nist1.datum.com
nist1-ny.glassey.com
nist1.aol-ca.truetime.com
ntp2.belbone.be
ntp.doubleukay.com
ntp0.cs.mu.OZ.AU
time.apple.com
0.pool.ntp.org
1.pool.ntp.org
2.pool.ntp.org
2.europe.pool.ntp.org
3.europe.pool.ntp.org
1.north-america.pool.ntp.org
2.north-america.pool.ntp.org
0.oceania.pool.ntp.org
1.oceania.pool.ntp.org
0.au.pool.ntp.org
3.au.pool.ntp.org
*/
typedef struct
{
unsigned char LiVnMode;
unsigned char Stratum;
unsigned char Poll;
unsigned char Precision;
long int RootDelay;
long int RootDispersion;
char RefID[4];
long int RefTimeInt;
long int RefTimeFraction;
long int OriTimeInt;
long int OriTimeFraction;
long int RecvTimeInt;
long int RecvTimeFraction;
long int TranTimeInt;
long int TranTimeFraction;
}STNP_Header;
int SYNC_Time(void);
#endif
ntp.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include "sntp.h"
int gethostIPbyname(char *name)
{
struct hostent *host=NULL;
struct in_addr *addr=NULL;
host = gethostbyname (name);
if(host->h_addr == NULL)
return -1;
addr = (struct in_addr*)host->h_addr;
return addr->s_addr;
}
int GetNTPTime(STNP_Header *H_SNTP)
{
int sockfd=0;
struct sockaddr_in server;
bzero((void*)H_SNTP, sizeof(STNP_Header));
H_SNTP->LiVnMode = 0x1b;
server.sin_family = AF_INET;
server.sin_addr.s_addr = gethostIPbyname(NTP_SERVER_NAME);
server.sin_port = htons(NTP_PORT);
if(-1 == (int)server.sin_addr.s_addr)
return -1;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd<0)
{
perror("sockfd");
return -1;
}
if(sendto(sockfd, (void*)H_SNTP, sizeof(STNP_Header), 0, (struct sockaddr*)&server, sizeof(server))<0)
{
perror("sendto");
return -1;
}
fd_set r;
FD_ZERO(&r);
FD_SET(sockfd, &r);
struct timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
if(1 != select(sockfd+1, &r, NULL, NULL, &timeout))
return -1;
if(recv(sockfd, (void*)H_SNTP, sizeof(STNP_Header), 0)<0)
return -1;
close(sockfd);
return 0;
}
int SYNC_Time(void)
{
STNP_Header HeaderSNTP;
time_t t1,t2,t3,t4,dis;
time(&t1);
t1+=JAN_1970;
printf("sync time from %s\n", NTP_SERVER_NAME);
if(GetNTPTime(&HeaderSNTP)<0)
return -1;
time(&t4);
t4+=JAN_1970;
t2 = ntohl(HeaderSNTP.RecvTimeInt);
t3 = ntohl(HeaderSNTP.TranTimeInt);
dis = ( (t2-t1)+(t3-t4) )/2;
if(dis<=0)
printf("local time is faster then server %d seconds\n", (int)-dis);
else
printf("local time is slower then server %d seconds\n", (int)dis);
struct timeval tv;
gettimeofday (&tv, 0);
tv.tv_sec+=dis;
printf("%s", ctime(&tv.tv_sec));
settimeofday (&tv, NULL);
return 0;
}
main.c
#include <stdio.h>
#include "sntp.h"
int main(void)
{
if(SYNC_Time()<0)
printf("sync time Failed!\n");
printf("sync time Succeed!\n");
return 0;
}