ITPub博客

首页 > 应用开发 > IT综合 > ntp协议及客户端开发

ntp协议及客户端开发

原创 IT综合 作者:helloxchen 时间:2010-11-01 13:40:16 0 删除 编辑

ntp协议及客户端开发

说明:
参考资料:ntp资料:http://ntp.buptnet.edu.cn/ntp_chinese/lesson/lesson.htm
开源代码:http://doolittle.icarus.com/ntpclient/
代码下载:
linux平台代码:http://pickup.mofile.com/7867848658129149
windows平台代码:http://pickup.mofile.com/0760743850823000

一、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、注意一些时间结构的区别。
[@more@]

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/24790158/viewspace-1040702/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
  • 博文量
    171
  • 访问量
    311121