无需 NTP 的小尺寸时钟同步
Posted
技术标签:
【中文标题】无需 NTP 的小尺寸时钟同步【英文标题】:Small footprint clock synchronization without NTP 【发布时间】:2011-08-04 07:37:12 【问题描述】:我正在寻找一种简单的时钟同步协议,该协议易于实现且占用空间小,并且在没有互联网连接的情况下也可以工作,因此可以用于例如在封闭的实验室网络中。需要明确的是,我不是在寻找可以仅用于排序事件的东西(如矢量时钟),而是可以使不同节点上的进程能够根据本地时钟同步它们的动作的东西。据我了解,这需要一个可以考虑时钟漂移的解决方案。可以假设存在 TCP/IP 或类似的相对低延迟的流连接。
【问题讨论】:
也许你可以解释一下为什么 NTP 不适合你的应用程序。 嗯,我认为这是因为 (1) NTP 实现很复杂,(2) 基本 NTP 设置涉及互联网服务器。我想避免复杂性和互联网访问的要求。 您可以运行自己的 NTP 服务器。这将问题从“重新实现NTP”减少到“安装和配置NTP”,后者更容易。 好的,你知道 NTP 客户端实现在“普通”处理器上的目标代码有多大吗? 为了澄清,要同步的节点之一可以是例如运行在奇异 RTOS 之上的低端处理器,内存很少 【参考方案1】:免责声明:无论如何,我都不是 NTP 专家。只是一个周末玩得开心的业余爱好者。
我知道您说过您不想要 NTP 实施,因为感知到的复杂性以及您的环境中可能无法使用 Internet NTP 服务器。
但是,简化的 NTP 查找可能很容易实现,如果您有本地 NTP 服务器,则可以实现良好的同步。
方法如下:
评论RFC 5905
您会看到 NTP v4 数据包如下所示:
LI(2 位) VN(3 位)- 使用 '100' (4) 模式(3 位) 层(8 位) 轮询(8 位) 精度(8 位) 根延迟(32 位) 根分散度(32 位) 参考 ID(32 位) 参考时间戳(64 位) 原始时间戳(64 位) 接收时间戳(64 位) 传输时间戳(64 位) 扩展字段 1(变量) 扩展字段 2(变量) ... 密钥标识符 摘要(128 位)摘要不是必需的,因此形成有效的客户端请求非常容易。按照 RFC 中的指导,使用 LI = '00',VN = '100'(十进制 4),Mode = '011'(十进制 3)。
用C#来说明:
byte[] ntpData = new byte[48]
Array.Clear(ntpData, 0, ntpData.Length);
ntpData[0] = 0x23; // LI = 00, VN = 100, Mode = 011
为您的目标服务器打开一个套接字并将其发送过来。
int ntpPort = 123;
IPEndPoint target = new IPEndPoint(Dns.GetHostEntry(serverDnsName).AddressList[0], ntpPort);
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
s.Connect(target);
s.Send(ntpData);
在响应中,当前时间将在传输时间戳中(字节 [40 - 48])。时间戳是 64 位无符号定点数。整数部分是前 32 位,小数部分是后 32 位。它表示自 1900 年 1 月 1 日 0h 以来的秒数。
s.Receive(ntpData);
s.Close();
ulong intPart = 0;
ulong fractPart = 0;
for (int i = 0; i < 4; i++)
intPart = (intPart << 8) | ntpData[40 + i];
for (int i = 4; i < 8; i++)
fractPart = (fractPart << 8) | ntpData[40 + i];
要以(大约)秒的粒度更新时钟,请使用:# of seconds since 0h Jan-1-1900 = intPart + (fractPart / 2^32)。 (我说粗略是因为没有考虑网络延迟,我们在这里四舍五入)
ulong seconds = intPart + (fractPart / 4294967296);
TimeSpan ts = TimeSpan.FromTicks((long)seconds * TimeSpan.TicksPerSecond);
DateTime now = new DateTime(1900, 1, 1);
now = DateTime.SpecifyKind(now, DateTimeKind.Utc);
now += ts;
“now”现在是带有当前时间的 DateTime,以 UTC 表示。
虽然这可能无法回答您的问题,但希望它能让 NTP 变得不那么不透明。 =)
【讨论】:
是的,但网络延迟正是问题所在,实际的 NTP 实现估计它,不是吗? 网络延迟本身不是问题。不对称或不可预测的网络延迟是。【参考方案2】:仅基于***文章,我就能够非常快速轻松地实现精确时间协议的精简版本。如果您感兴趣的只是将它们彼此同步而不是与外部世界同步,那么您应该能够以最小的努力获得毫秒精度。
协议的基本原理包括以下内容:
-
主时钟广播一条同步消息,其中包含他发送消息时的时间戳 (T1)。
客户端将收到同步消息的时间记录为 T1'。
客户端向主服务器发送延迟请求,并将其发送消息的时间记录为 T2。
主机以他收到消息的时间响应延迟请求。这次是 T2'。
客户将时钟调整为 (T1' - T1 - T2' + T2)/2。
如果您需要更好的稳定性,您可以实施锁相环或线性回归或类似的东西,以更好地控制您的抖动并避免由于网络滞后而导致的大幅波动。协议规定了许多更复杂的功能,但是否要实现它们取决于“足够好”的接近程度。
【讨论】:
【参考方案3】:ntp 是适合这项工作的工具。您不需要互联网连接,只需额外支付 105 美元和几个小时的生活时间,您甚至可以在没有互联网连接的情况下进行 GPS 同步以获得绝对时间参考,尽管这对您来说似乎并不重要。
忽略 GPS 同步的轻微额外复杂性,您可以使用几行配置文件(每个客户端上四行,服务器上五行)与所选系统的时钟同步。 ntpd 二进制文件在我的系统上是 505kB。您还可以使用可以定期运行的 ntpdate 来调整系统时钟(客户端上的零配置行,而不是使用正确参数调用 ntpdate 应用程序)。该二进制文件为 80kb。有一个 SNTP 协议允许嵌入式应用程序占用更小的空间(与普通的 ntp 服务器通信)。还有一个替代的 NTP 实现,称为 chrony。
还有一个名为 rdate 的程序(通常只在较旧的系统上,尽管source 可用)它的工作方式有点像 ntpdate 但不太精确。您还需要一个 RFC 868 服务器,通常在 inetd 中提供。
唯一的其他选择是已经提到的精确时间协议。
【讨论】:
【参考方案4】:Precision Time Protocol 可能符合要求吗?它看起来并不简单,但它似乎或多或少地完全符合您的要求。 (***页面上引用了一些开源实现。)
我认为问题在于这是一个固有的棘手问题,因此解决方案往往很复杂。 NTP 试图提供正确的绝对时间,这肯定超出了您的需要,但它确实具有众所周知和广泛实施的优势。
【讨论】:
【参考方案5】:使用http://www.ietf.org/rfc/rfc5905.txt 是否合适?
即使它远远超出了您的需要,您当然可以实现一个与 NTP 服务器一起工作的“兼容”客户端(即使您运行自己的 NTP 服务器),但是客户端实现在哪里故意天真?
例如,如果您不关心小的时间调整,请不要实施它们。如果您不关心双向同步,请不要实现,等等。
(请注意:RFC 中提供的大部分功能都是有原因的 - 准确的时间同步存在许多缺陷 - 包括如果时间突然改变,许多操作系统不喜欢它)
【讨论】:
【参考方案6】:这不是一个真正正确的答案,只是提醒您确保您准确了解硬件时钟源是什么以及有关它们的任何警告 - 特别是如果您计划使用一些稍微奇怪的可能性,例如低功耗 CPU /您提到的 RTOS 组合。
即使是 x86 机箱也至少有 2 或 3 个时钟可以使用,具体取决于设置 - 所有这些都具有不同的属性。
【讨论】:
以上是关于无需 NTP 的小尺寸时钟同步的主要内容,如果未能解决你的问题,请参考以下文章