一.NTP基础

1.NTP定义

NTP(Network Time Protocol,网络时间协议)是由RFC 1305定义的时间同步协议,用于分布式设备(比如电脑、手机、智能手表等)进行时间同步,避免人工校时的繁琐和由此引入的误差,方便快捷地实现多设备时间同步。

NTP校时服务基于UDP传输协议进行报文传输,工作端口默认为123/udp。

NTP使用协调世界时(UTC)以极高的精度同步计算机时钟时间,例如在局域网(LAN)中低至1毫秒,在互联网上则在数十毫秒内。

2.NTP 网络结构

服务器按层级关系连接,每一级称为一个层数(stratum),如主时间服务器层数为 stratum 1,二级时间服务器层数为 stratum 2,以此类推。时钟层数越大,准确性越低。

在局域网(LAN)中低至1毫秒,在互联网上则在数十毫秒内。

QT实现NTP功能-LMLPHP

3.NTP 报文格式

NTP有两种不同类型的报文,一种是时钟同步报文,另一种是控制报文。控制报文仅用于需要网络管理的场合,它对于时钟同步功能来说并不是必需的,这里不做介绍。

时钟同步报文封装在UDP报文中,其格式如图所示:

QT实现NTP功能-LMLPHP

各主要字段解释如下:

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

VN (Version Number): 长度为3比特,表示NTP的版本号.

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:验证信息。

其中,NTP发送和接收的报文数据包类似,通常只需要前48个字节就能进行授时和校时服务。下面分别是抓包获取的NTP请求数据包和回复数据包示例(仅前48个字节):

请求数据包:

1B 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 00 00 00 00 00 00 00 00 00 ------------头部

D0 AF 5F F5 23 D7 08 00---------------Originate Timestamp ()出发时间戳有效位为8 bytes

回复数据包:

1C 02 00 EC 00 00 06 EA 00 00 0C A2 C0 A8 33 CA----数据包头部,共16 bytes

D0 AF 5E A3 F5 BD 72 BC---------------Reference Timestamp

D0 AF 5F F5 23 D7 08 00---------------Originate Timestamp ()

D0 AF 61 D7 CD 2E F9 11---------------Receive Timestamp ()

D0 AF 61 D7 CD 2F F4 BA---------------Transmit Timestamp ()

收到数据包后,接收端本地再产生一个时间戳()。

这里,每个返回数据前4字节为秒的整数部分,后4字节为秒的小数部分。

4.NTP时钟服务器

下面补充一些常用的NTP时钟服务器:

名称            地址

北斗授时服务     114.255.121.193

清华TUNA协会     ntp.tuna.tsinghua.edu.cn

国家NTP授时     ntp.ntsc.ac.cn

中国NTP快速授时  cn.ntp.org.cn

教育网NTP授时  edu.ntp.org.cn

更多NTP授时服务器请查看:

http://www.ntp.org.cn/pool

https://dns.iui.im/ntp/

pool.ntp.org: the internet cluster of ntp servers

二.QT代码示例

  1. UDP请求包

void NtpClient::sendData()

{

    qint8 LI = 0;

    qint8 VN = 3;

    qint8 MODE = 3;

    qint8 STRATUM = 0;

    qint8 POLL = 4;

    qint8 PREC = -6;

    QDateTime epoch(QDate(1900, 1, 1), QTime(0, 0, 0));

    qint32 second = quint32(epoch.secsTo(QDateTime::currentDateTime()));

    qint32 temp = 0;

    QByteArray timeRequest(48, 0);

    timeRequest[0] = (LI << 6) | (VN << 3) | (MODE);

    timeRequest[1] = STRATUM;

    timeRequest[2] = POLL;

    timeRequest[3] = PREC & 0xff;

    timeRequest[5] = 1;

    timeRequest[9] = 1;

    timeRequest[40] = (temp = (second & 0xff000000) >> 24);

    temp = 0;

    timeRequest[41] = (temp = (second & 0x00ff0000) >> 16);

    temp = 0;

    timeRequest[42] = (temp = (second & 0x0000ff00) >> 8);

    temp = 0;

    timeRequest[43] = ((second & 0x000000ff));

    udpSocket->write(timeRequest);

}

  1. UDP接收报文及解析

void NtpClient::readData()

{

    QByteArray newTime;

    QDateTime epoch(QDate(1900, 1, 1), QTime(0, 0, 0));

    QDateTime unixStart(QDate(1970, 1, 1), QTime(0, 0, 0));

    while (udpSocket->hasPendingDatagrams()) {

        newTime.resize(udpSocket->pendingDatagramSize());

        udpSocket->read(newTime.data(), newTime.size());

    };

    QByteArray transmitTimeStamp ;

    transmitTimeStamp = newTime.right(8);

    quint32 seconds = transmitTimeStamp.at(0);

    quint8 temp = 0;

    for (int i = 1; i <= 3; ++i) {

        seconds = (seconds << 8);

        temp = transmitTimeStamp.at(i);

        seconds = seconds + temp;

    }

    QDateTime dateTime;

    uint secs = seconds - epoch.secsTo(unixStart);

    dateTime.setTime_t(secs);

    udpSocket->disconnectFromHost();

    if (dateTime.isValid()) {

        Q_EMIT receiveTime(dateTime);

    }

}

04-06 15:18