ntp協議及客戶端開發
說明:
參考資料:ntp資料:
開原始碼:
程式碼下載:
linux平臺程式碼:
windows平臺程式碼:
一、ntp介紹:
Network Time Protocol(NTP)是用來使計算機時間同步化的一種協議,它可以使計算機對其伺服器或時鐘源(如石英鐘,GPS等等)做同步
化,它可以提供高精準度的時間校正(LAN上與標準間差小於1毫秒,WAN上幾十毫秒),且可介由加密確認的方式來防止惡毒的協議攻擊。
二、ntp協議格式:
NTP packet = NTP header + Four TimeStamps = 48byte
NTP header : 16byte
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|LI | VN |Mode | Stratum | Poll | Precision | Root Delay | Root Dispersion | Reference Identifier |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
LeapYearIndicator : 2bit
VersionNumber : 3bit
Stratum : 8bit
Mode : 3 bit
PollInterval : 8 bit
Percision : 8bit
Root delay : 32bit
Root Dispersion : 32bit
Reference Identifier : 32bit
Four TimeStamps : 32byte
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
| Reference Timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
Reference Timestamp : 64bit
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
| Originate Timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
Originate Timestamp : 64bit
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
| Receive Timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
Receive Timestamp : 64bit
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
| Transmit Timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
Transmit Timestamp : 64bit
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
| Authenticator (optional) (96) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
以下為我捕獲的ntp客戶端與服務端互動的協議包:
NTP client send packet:
/* NTP header: 16 bytes */
1B 00 04 FA 00 01 00 00 00 01 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
/* Originate Timestamp */
CA 10 E7 F9 00 10 62 4D
NTP server response packet:
/* NTP header: 16 bytes */
1C 02 04 EC 00 00 00 29 00 00 07 AF CA 70 01 22
/* Reference Timestamp */
CA 10 E5 43 4D FA C7 59
/* Originate Timestamp */
CA 10 E7 F9 00 10 62 4D
/* Receive Timestamp */
CA 10 E7 F8 F0 7A 59 74
/* Transmit Timestamp */
CA 10 E7 F8 F0 7B 42 AB
三、ntp客戶端程式碼:與ntp服務端通訊,獲取時間資訊並更新本地時間
(注:程式碼經過精簡,刪除了一些變數定義及錯誤處理,完整程式碼可以下載)
linux平臺程式碼:
#define NTP_SERVER "time.buptnet.edu.cn"
#define NTP_PORT 123
#define JAN_1970 0x83aa7e80 /* 2208988800 1970 - 1900 in seconds */
#define NTPFRAC(x) (4294 * (x) + ((1981 * (x))>>11))
#define USEC(x) (((x) >> 12) - 759 * ((((x) >> 10) + 32768) >> 16))
struct ntptime
{
unsigned int coarse;
unsigned int fine;
};
void send_packet(int fd)
{
unsigned int data[12];
struct timeval now;
int ret;
#define LI 0
#define VN 3
#define MODE 3
#define STRATUM 0
#define POLL 4
#define PREC -6
if (sizeof(data) != 48)
{
fprintf(stderr,"size error ");
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); /* Root Delay (seconds) */
data[2] = htonl(1<<16); /* Root Dispersion (seconds) */
gettimeofday(&now, NULL);
data[10] = htonl(now.tv_sec + JAN_1970); /* Transmit Timestamp coarse */
data[11] = htonl(NTPFRAC(now.tv_usec)); /* Transmit Timestamp fine */
send(fd, data, 48, 0);
}
void get_packet_timestamp(int usd, struct ntptime *udp_arrival_ntp)
{
struct timeval udp_arrival;
gettimeofday(&udp_arrival, NULL);
udp_arrival_ntp->coarse = udp_arrival.tv_sec + JAN_1970;
udp_arrival_ntp->fine = NTPFRAC(udp_arrival.tv_usec);
}
void rfc1305print(unsigned int *data, struct ntptime *arrival, struct timeval* tv)
{
int li, vn, mode, stratum, poll, prec;
int delay, disp, refid;
struct ntptime reftime, orgtime, rectime, xmttime;
struct tm *ltm;
#define Data(i) ntohl(((unsigned int *)data)[i])
li = Data(0) >> 30 & 0x03;
vn = Data(0) >> 27 & 0x07;
mode = Data(0) >> 24 & 0x07;
stratum = Data(0) >> 16 & 0xff;
poll = Data(0) >> 8 & 0xff;
prec = Data(0) & 0xff;
if (prec & 0x80) prec|=0xffffff00;
delay = Data(1);
disp = Data(2);
refid = Data(3);
reftime.coarse = Data(4);
reftime.fine = Data(5);
orgtime.coarse = Data(6);
orgtime.fine = Data(7);
rectime.coarse = Data(8);
rectime.fine = Data(9);
xmttime.coarse = Data(10);
xmttime.fine = Data(11);
#undef Data
tv->tv_sec = xmttime.coarse - JAN_1970;
tv->tv_usec = USEC(xmttime.fine);
}
void set_local_time(struct timeval tv)
{
/* need root user. */
if (0 != getuid() && 0 != geteuid())
return;
settimeofday(&tv, NULL);
}
int main(void)
{
/* create socket. */
sock = socket(PF_INET, SOCK_DGRAM, 0);
/* bind local address. */
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);
bind(sock, (struct sockaddr*)&addr_src, addr_len);
/* connect to ntp server. */
memset(&addr_dst, 0, addr_len);
addr_dst.sin_family = AF_INET;
{
struct hostent* host = gethostbyname(NTP_SERVER);
memcpy(&(addr_dst.sin_addr.s_addr), host->h_addr_list[0], 4);
}
addr_dst.sin_port = htons(NTP_PORT);
connect(sock, (struct sockaddr*)&addr_dst, addr_len);
while (1)
{
fd_set fds_read;
struct timeval timeout;
int ret;
unsigned int buf[12];
int len;
struct sockaddr server;
socklen_t svr_len;
struct ntptime arrival_ntp;
struct timeval newtime;
FD_ZERO(&fds_read);
FD_SET(sock, &fds_read);
timeout.tv_sec = 6;
timeout.tv_usec = 0;
ret = select(sock + 1, &fds_read, NULL, NULL, &timeout);
if (0 == ret || !FD_ISSET(sock, &fds_read))
{
/* send ntp protocol packet. */
send_packet(sock);
continue;
}
/* recv ntp server's response. */
recvfrom(sock, buf, sizeof(buf), 0, &server, &svr_len);
/* get local timestamp. */
get_packet_timestamp(sock, &arrival_ntp);
/* get server's time and print it. */
rfc1305print(buf, &arrival_ntp, &newtime);
/* set local time to the server's time, if you're a root user. */
set_local_time(newtime);
}
close(sock);
return 0;
}
windows平臺程式碼跟linux平臺程式碼差不多,除了注意幾個地方:
1、recvfrom的引數svr_len在windows下必須賦值,否則報無效指標:int svr_len = sizeof(struct sockaddr);
2、windows下用_ftime函式來代替gettimeofday函式;
3、windows下用SetLocalTime來代替settimeofday函式;
4、注意一些時間結構的區別。