简单的NTP客户端-C语言实现

本文介绍了如何使用C语言实现一个简单的NTP客户端,基于NTPv3单播模式,详细讲解了NTP协议、报文格式和时间戳,并提供了实现分析及完整源码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

创建时间: 2017-09-11
最后修改时间: 2017-09-11

因个人水平有限,文章中存在不足,错误之处,还望指正

实验环境
Linux 2.6.32
gcc version 4.4.6 20120305 (Red Hat 4.4.6-4) (GCC)

引言

本NTP客户端实现是基于NTPv3单播模式来实现的,其中参考了SNTP的实现(SNTP为NTP的简化版)。

要完成客户端的开发需要准备一些知识,比如NTP工作模式,NTP报文格式等等,大体了解完这些后才能更好地掌握整个开发过程。

NTP协议

简介

NTP(Network Time Protocol),网络时间协议,应用于分布式时间服务器和客户端之间,实现客户端和服务器之间的时间同步,从而使网络内所有设备的时间基本保持一致。NTP工作于UDP的123端口。

NTP报文格式
0   2     5     8               16              24              32
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|LI | VN  |Mode |    Stratum    |     Poll      |   Precision   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                          Root Delay                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       Root Dispersion                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                     Reference Identifier                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                   Reference Timestamp (64)                    |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                   Originate Timestamp (64)                    |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                   Receive Timestamp (64)                      |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                   Transmit Timestamp (64)                     |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                  Authentication (optional) (64)               |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

                            NTPv3报文格式
  • Leap Indicator(LI)
    闰秒指示符,这是一个2位的代码,用于警示在当天的最后一分钟里插入或删除的闰秒。取值如下:

    0       无预告
    1       最近一分钟有61秒
    2       最近一分钟有59秒
    3       警告状态(时钟未同步)
    
  • Version Number(VN)
    版本号,这是一个3位的整数,用于表示NTP的版本。

  • Mode
    模式,这是一个3位的整数,表示模式,值定义如下:

    0           保留
    1           对称主动
    2           对称被动
    3           客户端
    4           服务器端
    5           广播
    6           为NTP控制控制消息
    7           为自用保留
    
  • Stratum
    本地时钟层级,这是一个八位无符号整数,表示本地时钟的层级,其值定义如下:

    0           未定义或难以获得
    1           主要参考(如无线电时钟钟,校正的院子时钟)
    2-255       第二参考(通过NTP或SNTP)
    
  • Poll
    轮询间隔,这是一个8位有符号整数,用于表示连续消息之间的最大间隔,以最接近2的N次幂来表示。如值为6表示2^6=64。

  • Precision
    本地时钟精度精度,这是一个8位有符号整数,用于表示本地时钟精度,以最接近2的N次幂来表示。

  • Root Delay
    这是一个32位有符号定点数,表示主要参考源的总往返时延,以秒为单位。该变量可以为正值和负值,具体取决于时间精度和偏移。

  • Root Dispersion
    这是一个32位有符号定点数,表示相对于主参考源的最大误差,以秒为单位,在15和16位之间。通常在该字段中出现的值范围为0到几百毫秒

  • Reference Identifier
    这是一个标识特定参考源的32位位串。在NTP版本3或版本4层级0或层级1服务器的情况下,这是一个4字符ASCII字符串,左对齐并且以0填充到32位。在NTP版本3辅助服务器中,这是参考源的32位IPv4地址。

  • Reference Timestamp
    这是以64位时间戳格式表示的上次设置或更正的本地时钟时间。

  • Original Timestamp
    这是以64位时间戳格式表示的请求离开客户端的时间。

  • Receive Timestamp
    这是以64位时间戳格式表示的请求到达服务器端的时间。

  • Transmit Timestamp
    这是以64位时间戳格式表示的应答离开服务器端的时间。

  • Authentication
    认证信息。

NTP时间戳
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                         Integer Part                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                         Fraction Part                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

                            NTP时间戳格式

NTP时间戳使用的是自1970-01-01所经过的秒数(单位为秒),它分为整数部分和小数部分。NTP时间戳整数部分与ICMP时间戳消息所使用的时间戳格式整数部分一致,但小数部分却是不同的。

在64位的NTP时间戳中,前32位为整数部分,后32位为小数部分,其转换如下:

frac * 1e6 / (2<<32)    
= frac /  4294.967296

根据以上转换规则可得NTP时间戳所能表示的最小精度为1 / 4294.967,296 = 0.2328307e-9,约等为0.232纳秒。

在32位的NTP时间戳中,前16位表示整数部分,后16位为小数部分,其转换与上面的类似:

frac * 1e6 / (2<<16)
= frac * 15.2587890625
NTP客户端操作

关于往返时延和本地时钟偏移的计算

为了计算相对于服务器的往返时延d和本地时钟偏移t,客户端根据客户端时钟设置请求中的发送时间戳。服务器将该字段复制到应答中的起始时间戳(Originate Timestamp),并根据服务器时钟设置接收时间戳(Receive Timestamp)和传送时间戳(Transmit Timestamp)。

当接收到服务器应答时,客户端根据NTP时间戳格式的时钟确定目的时间戳变量为到达时间。以下总结了四个时间戳:

Originate Timestamp     T1      客户端发送时间请求的时间
Receive Timestamp       T2      服务器收到时间请求的时间
Transmit Timestamp      T3      服务器发送时间回复的时间
Destination Timestamp   T4      客户端收到时间回复的时间

往返时延d和本地时钟偏移t定义为:

d = (T4 - T1) - (T2 - T3)
t = ((T2 - T1) + (T3 - T4)) / 2

实现(C语言)

实现分析

为了方便对NTP报文进行操作,自定义了ntphdr结构体,如下:


/* 32位时间戳 */
struct s_fixedpt {
    uint16_t    intpart;
    uint16_t    fracpart;
};

/* 64位时间戳 */
struct l_fixedpt {
    uint32_t    intpart;
    uint32_t    fracpart;
};

struct ntphdr {
#if __BYTE_ORDER == __BID_ENDIAN
    unsigned 
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值