NTP
一、NTP通信概述
1.1 NTP
网络时间协议(英语:Network Time Protocol,简称NTP)是在数据网络潜伏时间可变的计算机系统之间通过分组交换进行时钟同步的一个网络协议。自1985年以来,NTP是仍在使用的最古老的互联网协议之一。NTP由特拉华大学的David L. Mills设计。 NTP意图将所有参与计算机的协调世界时(UTC)时间同步到几毫秒的误差内。它使用Marzullo算法的修改版来选择准确的时间服务器,其设计旨在减轻可变网络延迟造成的影响。NTP通常可以在公共互联网保持几十毫秒的误差,并且在理想的局域网环境中可以实现超过1毫秒的精度。不对称路由和拥塞控制可能导致100毫秒(或更高)的错误。 该协议通常描述为一种主从式架构,但它也可以用在点对点网上中,对等体双方可将另一端认定为潜在的时间源。发送和接收时间戳采用用户数据报协议(UDP)的通信端口123实现。这也可以使用广播或多播,其中的客户端在最初的往返校准交换后被动地监听时间更新。NTP提供一个即将到来闰秒调整的警告,但不会传输有关本地时区或夏时制的信息。
1.2 SNTP
简单网络时间协议(Simple Network Time Protocol),由 NTP 改编而来,主要用来同步因特网中的计算机时钟。在 RFC2030 中定义。 SNTP是简化版的NTP,NTP(Network Time Protocol,网络时间协议)是用于同步系统时间的协议。它通过网络连接多个设备,确保这些设备的系统时钟保持一致。SNTP相比较NTP主要区别在于精确度和复杂性。SNTP通常用于不需要高精度同步的设备。而NTP则用于要求较高时间精度的场景。
虽然现在的程序名字使用的是SNTP,但现在底层的实现已经改成NTP了,只是名字还没改过来。对模组来说, 没多大区别。
二、演示功能概述
本demo将演示使用NTP授时服务来同步时间。
三、准备硬件环境
参考:硬件环境清单第二章节内容,准备以及组装好硬件环境。
四、软件环境
在开始实践本示例之前,先筹备一下软件环境:
1. Luatools工具;
2. 内核固件文件(底层core固件文件):LuatOS-SoC_V2003_Air780EPM;参考项目使用的内核固件;
3. luatos需要的脚本和资源文件
脚本和资源文件:点我,查看demo链接
lib脚本文件:使用Luatools烧录时,勾选 添加默认lib 选项,使用默认lib脚本文件;
准备好软件环境之后,接下来查看如何烧录项目文件到Air780EPM开发板,将本篇文章中演示使用的项目文件烧录到Air780EP开发板中。
4. LuatOS 工程文件:ntp_20250327.luatos
五、代码示例介绍
5.1 API说明
5.1.1 socket.ntptm()
网络对时后的时间戳(ms级别)
- 参数
无
- 返回值
返回值类型 |
释义 |
---|---|
table |
包含时间信息的数据 |
- 例子
-- 本API于 2023.11.15 新增
-- 注意, 本函数在执行socket.sntp()且获取到NTP时间后才有效
-- 而且是2次sntp之后才是比较准确的值
-- 网络波动越小, 该时间戳越稳定
local tm = socket.ntptm()
-- 对应的table包含多个数据, 均为整数值
-- 标准数据
-- tsec 当前秒数,从1900.1.1 0:0:0 开始算, UTC时间
-- tms 当前毫秒数
-- vaild 是否有效, true 或者 nil
-- 调试数据, 调试用,一般用户不用管
-- ndelay 网络延时平均值,单位毫秒
-- ssec 系统启动时刻与1900.1.1 0:0:0的秒数偏移量
-- sms 系统启动时刻与1900.1.1 0:0:0的毫秒偏移量
-- lsec 本地秒数计数器,基于mcu.tick64()
-- lms 本地毫秒数计数器,基于mcu.tick64()
log.info("tm数据", json.encode(tm))
log.info("时间戳", string.format("%u.%03d", tm.tsec, tm.tms))
5.1.2 socket.sntp_port(port)
设置SNTP服务器的端口号
- 参数
传入值类型 |
释义 |
---|---|
int |
port 端口号, 默认123 |
- 返回值
返回值类型 |
释义 |
---|---|
int |
返回当前的端口号 |
- 例子
-- 本函数于2024.5.17新增 -- 大部分情况下不需要设置NTP服务器的端口号,默认123即可
5.2 使用NTP授时服务来同步时间
5.2.1 demo 介绍
-- 开始时间同步
socket.sntp()
-- 自定义ntp地址
-- socket.sntp("ntp.aliyun.com")
-- socket.sntp({"baidu.com", "abc.com", "ntp.air32.cn"})
-- 通常只需要几百毫秒就能成功,超时时间2s
local ret = sys.waitUntil("NTP_UPDATE", 5000)
if ret then
-- 以下是获取/打印时间的演示,注意时区问题
log.info("sntp", "时间同步成功", "本地时间", os.date())
log.info("sntp", "时间同步成功", "UTC时间", os.date("!%c"))
log.info("sntp", "时间同步成功", "RTC时钟(UTC时间)", json.encode(rtc.get()))
-- os.time(rtc.get()) 需要 2023.07.21 之后的版本, 因为月份的命名差异mon/month
-- log.info("sntp", "时间同步成功", "utc时间戳", os.time(rtc.get()))
log.info("sntp", "时间同步成功", "本地时间戳", os.time())
local t = os.date("*t")
log.info("sntp", "时间同步成功", "本地时间os.date() json格式", json.encode(t))
log.info("sntp", "时间同步成功", "本地时间os.date(os.time())", os.time(t))
-- log.info("sntp", "时间同步成功", "本地时间", os.time())
-- 正常使用, 一小时一次, 已经足够了, 甚至1天一次也可以
-- sys.wait(3600000)
-- 这里为了演示, 用5秒一次
sys.wait(5000)
else
log.info("sntp", "时间同步失败")
sys.wait(60000) -- 1分钟后重试
end
-- 时间戳, 精确到毫秒. 2023.11.15 新增
-- 注意, 至少成功完成2次sntp,该时间戳才比较准确
-- 如果仅完成了一次sntp, 时间戳比标准时间会慢一个网络延时的时长(10~500ms)不等
if socket.ntptm then
local tm = socket.ntptm()
log.info("tm数据", json.encode(tm))
log.info("时间戳", string.format("%u.%03d", tm.tsec, tm.tms))
sys.wait(5000)
end
end
end)
-- 订阅ntp同步时间失败的消息
sys.subscribe("NTP_ERROR", function()
log.info("socket", "sntp error")
-- socket.sntp()
end)
六、功能验证
七、总结
本 demo 介绍了如何使用NTP服务器同步时间。
扩展
常见问题
1、NTP同步时间后,这个clock精度高吗,需要多久同步一次
并不能保证任何时间任何地点都能百分百同步到正确的时间。 所以,如果用户项目中的业务逻辑严格依赖于时间同步功能 则不要使用本功能模块,建议使用自己的应用服务器来同步时间。
2、多长时间NTP同步一次
正常使用, 一小时一次, 已经足够了, 甚至 1 天一次也可以。
3、这个函数 socket.sntp()后每次在程序中调用 os.time 也是实时时间了吗?
是的,只要时间同步成功了,就是实时时间了。