网络时间协议(NTP)的作用和推导

NTP网络时间协议, 就是为了计算出设备之间的时间差。根据NTP方式计算出来的时间差, 用户就可以把设备设置成相同的时间。

比如,设备A现在的时间是10:00:00, 而设备B的时间是11:00:00。 此时如果你想让设备A校对成设备B的时间。那么就是需要NTP网络时间协议,计算出设备之间的时间差后,给设备A设上去就好了。

误差依旧是存在的,因为设备A-----》设备B的网络时间,和设备B-----》设备A的网络时间, 是不一样的。两者的差值就是误差。

 

下面这张图解释了设备A和设备B之间如何通过NTP网络时间协议同步时间的。

 

推导的主要内容引至其他作者, 讲的比较透彻。

下面说一下NTP计算的过程:

如下图所示,客户机首先向服务器发送一个NTP 包,其中包含了该包离开客户机的时间戳T1,当服务器接收到该包时,依次填入包到达的时间戳T2、包离开的时间戳T3,然后立即把包返回给客户机。客户机在接收到响应包时,记录包返回的时间戳T4。客户机用上述4个时间参数就能够计算出2个关键参数:NTP包的往返延迟d和客户机与服务器之间的时钟偏差t。客户机使用时钟偏差来调整本地时钟,以使其时间与服务器时间一致。

图中:T1为客户发送NTP请求时间戳(以客户时间为参照);T2为服务器收到NTP请求时间戳(以服务器时间为参照);T3为服务器回复NTP请求时间戳(以服务器时间为参照);T4为客户收到NTP回复包时间戳(以客户时间为参照);d1为NTP请求包传送延时,d2为NTP回复包传送延时;t为服务器和客户端之间的时间偏差,d为NTP包的往返时间。

现已知T1、T2、T3、T4,可以求得t,以调整客户方时钟:

....................................................式(1)

假设NPT请求和回复包传送延时相等,即d1=d2,则可解得:

.....................................式(2)

根据式(1),t也可表示为:t=(T2-T1)+d1=(T2-T1)+d/2.....................式(3)

可以看出,t、d只与T2、T1差值及T3、T4差值相关,而与T2、T3差值无关,即最终的结果与服务器处理请求所需的时间无关。因此,客户端即可通过T1、T2、T3、T4计算出时差t去调整本地时钟。

以上结论的推导过程是这样的:(假设d1=d2,是误差来源)

NTP授时精度

上面的推导可以看出,假设d1=d2,是误差来源。

NTP授时精度与NTP服务器与用户间的网络状况有关,主要取决于NTP包往返路由的延时对称程度,往返路由的延时不对称值最大不超过网络延时。式(2)是在假设NTP请求和回复包在网上传送延时相等,即d1=d2=d/2的情况下得出的,而d1、d2的取值范围在(0...d)间,由式(3)可以得出最大授时误差是±d/2。一般广域网的网络延时在10 ms~500ms之间;所以误差是5ms~250ms。局域网的网络延时在计时操作系统内核处理延迟的情况下通常小于1ms。

假定局域网内NTP延时小于1ms,理论上授时误差小于0.5ms,但对于Windows操作系统内置的NTP客户和NTP服务,并不能达到此精度。Windows NTP时钟分辨率因操作系统和硬件不同而有所不同,时钟分辨率通常为10ms或15ms。基于Windows操作系统内置的NTP授时精度最高不超过10ms。

 

推导内容引至: https://www.vfe.ac.cn/NewsDetail-2332.aspx, 更详细的内容,比如如何做更加精确等,可以参考这篇文章。我这里是从便于开发者理解的角度出发总结了一下。上面的连接可能打不开,可以看转发的连接:https://www.cnblogs.com/sheng1255blog/p/5121979.html

NTP的报文格式

主要字段的解释如下:

  • LI(Leap Indicator):长度为2比特,值为"11"时表示告警状态,时钟未被同步。为其他值时NTP本身不做处理。

  • VN(Version Number):长度为3比特,表示NTP的版本号,目前的最新版本为3。

  • Mode:长度为3比特,表示NTP的工作模式。不同的值所表示的含义分别是:0未定义、1表示主动对等体模式、2表示被动对等体模式、3表示客户模式、4表示服务器模式、5表示广播模式或组播模式、6表示此报文为NTP控制报文、7预留给内部使用。

  • Stratum:系统时钟的层数,取值范围为1~16,它定义了时钟的准确度。层数为1的时钟准确度最高,准确度从1到16依次递减,层数为16的时钟处于未同步状态,不能作为参考时钟。

  • Poll:轮询时间,即两个连续NTP报文之间的时间间隔。

  • Precision:系统时钟的精度。

  • Root Delay:本地到主参考时钟源的往返时间。

  • Root Dispersion:系统时钟相对于主参考时钟的最大误差。

  • Reference Identifier:参考时钟源的标识。

  • Reference Timestamp:系统时钟最后一次被设定或更新的时间。

  • Originate Timestamp:NTP请求报文离开发送端时发送端的本地时间。

  • Receive Timestamp:NTP请求报文到达接收端时接收端的本地时间。

  • Transmit Timestamp:应答报文离开应答者时应答者的本地时间。

  • Authenticator:验证信息。

如何使用:

raop_rtp_thread_time(void *arg) {
    raop_rtp_t *raop_rtp = arg;
    assert(raop_rtp);
    struct sockaddr_storage saddr;
    socklen_t saddrlen;
    unsigned char packet[128];
    unsigned int packetlen;
    unsigned char time[32] = {0x80, 0xd2, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    };
    uint64_t base = now_us();
    uint64_t rec_pts = 0;
    while (1) {
        logger_log(raop_rtp->logger, LOGGER_DEBUG, "httpd_thread 11.15.4.1");
        MUTEX_LOCK(raop_rtp->run_mutex);
        if (!raop_rtp->running) {
            MUTEX_UNLOCK(raop_rtp->run_mutex);
            break;
        }
        MUTEX_UNLOCK(raop_rtp->run_mutex);
        uint64_t send_time = now_us() - base + rec_pts;
        logger_log(raop_rtp->logger, LOGGER_DEBUG, "httpd_thread 11.15.4.2");
        byteutils_put_timeStamp(time, 24, send_time);
        logger_log(raop_rtp->logger, LOGGER_DEBUG,
                   "raop_rtp_thread_time send time 32 bytes, port = %d", raop_rtp->timing_rport);
        struct sockaddr_in *addr = (struct sockaddr_in *) &raop_rtp->remote_saddr;
        addr->sin_port = htons(raop_rtp->timing_rport);
        int sendlen = sendto(raop_rtp->tsock, (char *) time, sizeof(time), 0,
                             (struct sockaddr *) &raop_rtp->remote_saddr,
                             raop_rtp->remote_saddr_len);
        logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp_thread_time sendlen = %d", sendlen);
        logger_log(raop_rtp->logger, LOGGER_DEBUG, "httpd_thread 11.15.4.3");
        saddrlen = sizeof(saddr);
        packetlen = recvfrom(raop_rtp->tsock, (char *) packet, sizeof(packet), 0,
                             (struct sockaddr *) &saddr, &saddrlen);
        int type_t = packet[1] & ~0x80;
        logger_log(raop_rtp->logger, LOGGER_DEBUG,
                   "raop_rtp_thread_time receive time type_t 0x%02x, packetlen = %d", type_t,
                   packetlen);
        if (type_t == 0x53) {

        }
        // 9-16 NTP请求报文离开发送端时发送端的本地时间。  T1
        uint64_t Origin_Timestamp = byteutils_read_timeStamp(packet, 8);
        // 17-24 NTP请求报文到达接收端时接收端的本地时间。 T2
        uint64_t Receive_Timestamp = byteutils_read_timeStamp(packet, 16);
        // 25-32 Transmit Timestamp:应答报文离开应答者时应答者的本地时间。 T3
        uint64_t Transmit_Timestamp = byteutils_read_timeStamp(packet, 24);
        logger_log(raop_rtp->logger, LOGGER_DEBUG, "httpd_thread 11.15.4.4");
        // FIXME: 先简单这样写吧
        rec_pts = Receive_Timestamp;

        struct timeval now;
        struct timespec outtime;
        MUTEX_LOCK(raop_rtp->time_mutex);
        logger_log(raop_rtp->logger, LOGGER_DEBUG, "httpd_thread 11.15.4.5");
        gettimeofday(&now, NULL);
        outtime.tv_sec = now.tv_sec + 3;
        outtime.tv_nsec = now.tv_usec * 1000;
        int ret = pthread_cond_timedwait(&raop_rtp->time_cond, &raop_rtp->time_mutex, &outtime);
        MUTEX_UNLOCK(raop_rtp->time_mutex);

        logger_log(raop_rtp->logger, LOGGER_DEBUG, "httpd_thread 11.15.4.6");
    }

    logger_log(raop_rtp->logger, LOGGER_INFO, "Exiting UDP raop_rtp_thread_time thread");
    return 0;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值