ntp.lua 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. --- 模块功能:网络授时.
  2. -- 重要提醒!!!!!!
  3. -- 本功能模块采用多个免费公共的NTP服务器来同步时间
  4. -- 并不能保证任何时间任何地点都能百分百同步到正确的时间
  5. -- 所以,如果用户项目中的业务逻辑严格依赖于时间同步功能
  6. -- 则不要使用使用本功能模块,建议使用自己的应用服务器来同步时间
  7. -- 参考 http://ask.openluat.com/article/912 加深对授时功能的理解
  8. -- @module ntp
  9. -- @author openLuat
  10. -- @license MIT
  11. -- @copyright openLuat
  12. -- @release 2017.10.21
  13. require "misc"
  14. require "socket"
  15. require "utils"
  16. require "log"
  17. local sbyte, ssub = string.byte, string.sub
  18. module(..., package.seeall)
  19. -- NTP服务器域名集合
  20. local timeServer = {
  21. "cn.pool.ntp.org",
  22. "edu.ntp.org.cn",
  23. "cn.ntp.org.cn",
  24. "s2c.time.edu.cn",
  25. "time1.aliyun.com",
  26. "tw.pool.ntp.org",
  27. "0.cn.pool.ntp.org",
  28. "0.tw.pool.ntp.org",
  29. "1.cn.pool.ntp.org",
  30. "1.tw.pool.ntp.org",
  31. "3.cn.pool.ntp.org",
  32. "3.tw.pool.ntp.org",
  33. }
  34. -- 同步超时等待时间
  35. local NTP_TIMEOUT = 8000
  36. -- 同步是否完成标记
  37. local ntpEnd = false
  38. -- 获取NTP服务器地址列表
  39. -- @return table,服务器地址列表
  40. -- @usage local addtable = ntp.getServers()
  41. function getServers()
  42. return timeServer
  43. end
  44. -- 设置NTP服务器地址列表
  45. -- @table st,tab类型,服务器地址列表
  46. -- @return 无
  47. -- @usage ntp.getServers({"1edu.ntp.org.cn","cn.ntp.org.cn"})
  48. function setServers(st)
  49. timeServer = st
  50. end
  51. -- NTP同步标志
  52. -- @return bool,NTP的同步状态true为成功,fasle为失败
  53. -- @usage local sta = ntp.isEnd()
  54. function isEnd()
  55. return ntpEnd
  56. end
  57. local sTs,sFnc,sFun
  58. -- 同步时间,随机每个NTP服务器尝试1次,超时8秒,适用于被任务函数调用
  59. -- @number ts,每隔ts小时同步1次
  60. -- @function fnc,同步成功后回调函数
  61. -- @function fun,同步成功前回调函数
  62. -- @return nil
  63. -- @usage ntp.ntpTime() -- 只同步1次
  64. -- @usage ntp.ntpTime(1) -- 1小时同步1次
  65. -- @usage ntp.ntpTime(nil,fnc) -- 只同步1次,同步成功后执行fnc()
  66. -- @usage ntp.ntpTime(24,fnc) -- 24小时同步1次,同步成功后执行fnc()
  67. function ntpTime(ts, fnc, fun)
  68. local rc, data, ntim
  69. local sTs,sFnc,sFun = ts or sTs, fnc or sFnc, fun or sFun
  70. ntpEnd = false
  71. while true do
  72. local tUnusedSvr = {}
  73. for i = 1, #timeServer do
  74. tUnusedSvr[i] = timeServer[i]
  75. end
  76. for i = 1, #timeServer do
  77. while not socket.isReady() do sys.waitUntil('IP_READY_IND') end
  78. local c = socket.udp()
  79. local idx = rtos.tick() % #tUnusedSvr + 1
  80. if c:connect(tUnusedSvr[idx], "123") then
  81. if c:send(string.fromHex("E30006EC0000000000000000314E31340000000000000000000000000000000000000000000000000000000000000000")) then
  82. rc, data = c:recv(NTP_TIMEOUT)
  83. if rc and #data == 48 then
  84. ntim = os.date("*t", (sbyte(ssub(data, 41, 41)) - 0x83) * 2 ^ 24 + (sbyte(ssub(data, 42, 42)) - 0xAA) * 2 ^ 16 + (sbyte(ssub(data, 43, 43)) - 0x7E) * 2 ^ 8 + (sbyte(ssub(data, 44, 44)) - 0x80) + 1)
  85. if type(sFun) == "function" then sFun() end
  86. misc.setClock(ntim, sFnc)
  87. ntpEnd = true
  88. c:close()
  89. break
  90. end
  91. end
  92. end
  93. local cnt, n, m = #tUnusedSvr, 1
  94. for m = 1, cnt do
  95. if m ~= idx then
  96. tUnusedSvr[n] = tUnusedSvr[m]
  97. n = n + 1
  98. end
  99. end
  100. tUnusedSvr[cnt] = nil
  101. c:close()
  102. sys.wait(1000)
  103. end
  104. if ntpEnd then
  105. sys.publish("NTP_SUCCEED")
  106. log.info("ntp.timeSync is date:", ntim.year .. "/" .. ntim.month .. "/" .. ntim.day .. "," .. ntim.hour .. ":" .. ntim.min .. ":" .. ntim.sec)
  107. else
  108. log.warn("ntp.timeSync is error!")
  109. end
  110. if sTs == nil or type(sTs) ~= "number" then break end
  111. sys.wait(sTs * 3600 * 1000)
  112. end
  113. end
  114. --- ntp同步时间任务.
  115. -- 重要提醒!!!!!!
  116. -- 本功能模块采用多个免费公共的NTP服务器来同步时间
  117. -- 并不能保证任何时间任何地点都能百分百同步到正确的时间
  118. -- 所以,如果用户项目中的业务逻辑严格依赖于时间同步功能
  119. -- 则不要使用使用本功能模块,建议使用自己的应用服务器来同步时间
  120. -- @number[opt=nil] period 调用本接口会立即同步一次;每隔period小时再自动同步1次,nil表示仅同步一次
  121. -- @function[opt=nil] fnc 同步结束,设置系统时间后的回调函数,回调函数的调用形式为:
  122. -- fnc(time,result)
  123. -- time表示设置之后的系统时间,table类型,例如{year=2017,month=2,day=14,hour=14,min=19,sec=23}
  124. -- result为true表示成功,false或者nil为失败
  125. -- @function[opt=nil] fun 同步结束,设置系统时间前的回调函数,回调函数的调用形式为:fun()
  126. -- @return nil
  127. --
  128. -- @usage
  129. -- 立即同步一次(仅同步这一次):
  130. -- ntp.timeSync()
  131. --
  132. -- 立即同步一次,之后每隔1小时自动同步一次:
  133. -- ntp.timeSync(1)
  134. --
  135. -- 立即同步一次(仅同步这一次),同步结束后执行fnc(time,result):
  136. -- ntp.timeSync(nil,fnc)
  137. --
  138. -- 立即同步一次,之后每隔24小时自动同步一次,每次同步结束后执行fnc(time,result):
  139. -- ntp.timeSync(24,fnc)
  140. function timeSync(period, fnc, fun)
  141. sTs,sFnc,sFun = period, fnc, fun
  142. sys.taskInit(ntpTime)
  143. end