当前校验逻辑: 本地和服务端的时间校验绑定在一个通用请求上,这个请求每七分钟会到服务端请求一次,本地拿到服务器时间后,计算请求服务器来回的时间,最后得出与服务器时间的差值,然后每次new Date().getTime() 时候都会加上这个差值,保证时间的准确性,时间精确度在毫秒。
问题描述:问题是出现在用户手动修改过本机时间后,第一个七分钟内时间都是错乱的,如果是七分钟后,再改回当前时间,又会有七分钟校验错误时间。
在网络上着找到了很多关于这方面校验的资料,总结出几种方法,关于本地和服务端交互:
1. 本地打开计时器,在与服务端校验完成后的七分钟内,都由自己本机去进行时间轮询,每次获取时间不new date,而是直接从计时器中得到当前的时间。
大概实现逻辑:
function beginTimer () { // 100ms loop once currentTime += 100; if (timerId) { clearTimeout(timerId); // clear previous. } timerId = setTimeout(function () { beginTimer(); }, 100); }
优点: 自己开的计时器,不受用户修改本机时间影响。
缺点: 缺点也很明显,setTimeOut 也只是加入到任务的最后,如果有其他任务在排队,它也只能等着,这样就会导致实际时间已经超过100ms,但是只加了100ms,此时获取到的时间就会有偏差,而且随着时间加长,偏差会越来越大,而且这样频繁的打开setTimeOut,说对网页性能完全没有影响,很显然不太现实。
2. 由于多端开发,参照手机端的校验方式是由后台唤醒之后,开始拿取离线消息,同时校验时间,我觉得也可以采用这样的一种方式,当程序被切到当前界面时,可以去与服务端校验一次时间,但PC端毕竟与手机相差很多,可以几秒内进行多次的,切进来,切出去,所以必然要有一种时间或者次数的限制,来避免自身频繁的进行服务器请求(未实现此方式,不贴代码了)
3. 这种方法跟第一种方式很像,但并不是自己开定时器来计算当前时间,而是针对自己修改了时间的用户,在开始new一个时间,然后每30秒,再次new一个时间,判断两个时间的差值,是否在我的预期范围内(误差10s),如果不在,则进行服务校验,这样就能保证用户在修改时间后的一分钟以内,我就能知道本机时间出现了问题,需要我来重新校验了。
大概实现逻辑:
var beginTime; var timerId; var defaultInterval = 30000; // check time difference.default: 30s. var mixTimeDiff = 10000; // local time mix differece is 10s. var flush = true; // a lock. for prevent timer loops during init Timer. function initTime () { flush = false; if (timerId) { clearTimeout(timerId); } beginTime = new Date().getTime(); beginTimer(); } function beginTimer () { let currentTime = new Date().getTime(); if (flush && Math.abs(currentTime - beginTime) > defaultInterval + mixTimeDiff) { // check serverTime. console.error('current time is wrong! Recheck the server time.'); } beginTime = currentTime; // reset begin time. if (timerId) { clearTimeout(timerId); // clear previous. } flush = true; // release lock. timerId = setTimeout(function () { if (flush) { beginTimer(); } }, defaultInterval); } module.exports = { initTime }
优点:每30进行一次校验,频率并不是很高,对性能影响上也不会很大,同样在30s内就能了解到本地时间是否正常。
缺点:方法有点取巧,并未完全解决用户修改本地时间的问题,在这三十秒内,还是会出现发送时间有误的问题,但是修改时间的需求毕竟是少数,能在30m内纠正好,聊胜于无了。