<源码>自己写个简单的单播SNTP客户端

12 篇文章 0 订阅

一、功能实现

以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;
}

配置windows 7为NTP服务器


SNTP协议客户端实现以及数据包格式: LI:跳跃指示器,警告在当月最后一天的最终时刻插入的迫近闺秒(闺秒)。 VN:版本号。 Mode:工作模式。该字段包括以下值:0-预留;1-对称行为;3-客户机;4-服务器;5-广播;6-NTP控制信息。NTP协议具有3种工作模式,分别为主/被动对称模式、客户/服务器模式、广播模式。在主/被动对称模式中,有一对一的连接,双方均可同步对方或被对方同步,先发出申请建立连接的一方工作在主动模式下,另一方工作在被动模式下; 客户/服务器模 式与主/被动模式基本相同,惟一区别在于客户方可被服务器同步,但服务器不能被客户同步;在广播模式中,有一对多的连接,服务器不论客户工作 在何种模式下,都会主动发出时间信息,客户根据此信息调整自己的时间。 Stratum:对本地时钟级别的整体识别。 Poll:有符号整数表示连续信息间的最大间隔。 Precision:有符号整数表示本地时钟精确度。 Root Delay:表示到达主参考源的一次往复的总延迟,它是有15~16位小数部分的符号定点小 数。 Root Dispersion:表示一次到达主参考源的标准误差,它是有15~16位小数部分的无符号 定点小数。 Reference Identifier:识别特殊参考源。 Originate Timestamp:这是向服务器请求分离客户机的时间,采用64位时标格式。 Receive Timestamp:这是向服务器请求到达客户机的时间,采用64位时标格式。 Transmit Timestamp:这是向客户机答复分离服务器的时间,采用64位时标格式。 Authenticator(Optional):当实现了NTP认证模式时,主要标识符和信息数字域就 包括已定义的信息认证代码(MAC)信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值