免责声明:无论如何,我都不是 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 变得不那么不透明。 =)