一、问题描述
需要解决的问题很简单,就是如何在页面上比较准确的显示服务器时间。目前比较常用的方法就是根据基准时间使用setTimeout或 setInterval来计算最新的时间,这样的问题在于setTimeout与setInterval的时间精度比较低,经测试一分钟大概能相差几秒 (与电脑性能以及运行的任务也相关),这样的精度在某些需求下是无法满足的。除此之外,如果要获得比较准确的时间可以定期与服务器进行校准,只是这样实现 的成本大一些。
本文尝试了一种改良的客户端实现时间同步的方式,具有以下的特点:
- 1. 根据基准时间进行纯客户端计算,无需服务器校准
- 2. 时间精度与客户端系统时间保持一致
- 3. 不受客户端时间与服务器时间不同步造成的影响
- 4. 不受客户端系统时间发生修改造成的影响
- 5. 不受页面前进后退造成的影响
二、具体实现
1. 为了解决原方案中的时间精度问题,这里不再使用setTimeout和setInterval来直接计算时间,而是直接使用客户端时间(CT)。不过客户 端时间很可能与服务器时间(ST)不同步,这需要在页面加载的时候计算出客户端与服务器的时间差值(ΔT),这样只需在客户端时间上做一下修正即可得到准 确的服务器时间(ST’ = CT - ΔT)。
2. 由于客户端时间很可能被用户修改,因此直接按照步骤1中的方式计算,一旦用户修改了时间,计算出来的服务器时间也将随之发生变化。这就需要检测出客户端时 间的变化并消除这个变化。检测的方法很简单,即在每个计算周期(T)都将当时的客户端时间(CT2)与上一个周期的客户端时间(CT1)做比较,一旦两个 周期的差值(ΔT’ = CT2 - CT1 - T)大于某个预设值(S)时就将差值(ΔT’)加入到ΔT中,即此时的ΔT = ΔT + ΔT’。之所以需要设置一个预设值,是因为每个周期的时间本身不是固定的(依赖于setTimeout),因此ΔT’并不会等于0,如果每次都将 setTimeout造成的误差作为CT与ST之间的误差将会造成计算不准确。经过以上的计算,用户修改时间后将不会对计算结果产生影响。
3. 经JK提醒,完成以上两步还有一个问题,当用户离开当前页面之后后退回页面时,时间计算不准确。问题在于基准时间是服务器给的,在第一次进入页面的时候确 定,当用户后退回当前页面时,基准时间并没有变,这样会导致重新从过期的基准时间开始计算,导致不准确。需要解决这个问题就是需要解决跨页面的数据存储问 题,这在之前的《Ajax应用中浏览器历史的兼容性解决方案》一 文中已经说明,即通过表单元素来记忆。具体的实现方案是,页面第一次加载时创建两个input,一个用于存储最近一次的客户端时间,一个用于存储最近一次 的基准时间。如果发现已经存在input(前进、后退、非强制刷新)则比较上一次的客户端时间与当前客户端时间,如果其差值大于某个预设值则像步骤2中一 样进行校准,只不过使用的将是最新的基准值。
具体的代码实现如下
- /*定义*/
- var SyncTimer = (function(){
- /*跨页面数据存储器*/
- //存储最近一次的客户端时间,用于在页面前进、后退时进行时间矫正
- var memoryElementID = 'sync_timer_memory_el';
- //存储矫正后的最新基准时间,当页面前进、后退到当前页面时会以此值为新的基准时间
- var memoryBaseTimeElementID = 'sync_timer_memory_base_time_el';
- document.write('
- <input type="text" id="' + memoryElementID + '">');
- document.write('
- <input type="text" id="' + memoryBaseTimeElementID + '">');
- return{
- /*
- * @param { Integer } baseTime 基准时间
- * @param { Function } updater 时间更新时的监听器
- * @param { Integer } interval 校准计算周期时长,默认为200ms。
- * @param { Integer } threshold 两个检查周期之间的时间误差(差值-周期时长)如果大于阈值则视为客户端时间有调整,默认为500ms。
- */
- run: function(baseTime,updater,interval,threshold){
- interval = interval || 200;
- threshold = threshold || 500;
- var memoryEl = document.getElementById(memoryElementID);
- var baseTimeEl = document.getElementById(memoryBaseTimeElementID);
- /*前进、后退或刷新,则矫正baseTime*/
- if( memoryEl.value != '' ){
- //计算当前客户端时间与上次存储的客户端时间之差,如果差值超过阈值则更新基准时间
- var diff = +new Date - parseInt(memoryEl.value);
- if( Math.abs( diff ) - interval > threshold ){
- baseTime = parseInt(baseTimeEl.value);
- baseTime += diff;
- }
- }
- var ct = +new Date;
- var diff = ct - baseTime;
- var pt = ct,cct;
- (function(){
- cct = +new Date;
- /*计算当前计算周期与上一个计算周期的时间差,如果差值大于设定的阈值则进行矫正(处理客户端时间调整的情况)*/
- var secDiff = cct - pt;
- if( Math.abs( secDiff ) - interval > threshold ){
- diff += (secDiff - interval);
- }
- var fixedTime = cct - diff;
- updater( fixedTime );
- pt = memoryEl.value = cct;
- baseTimeEl.value = fixedTime;
- setTimeout(arguments.callee,interval);
- })();
- }
- }
- })();
- /*使用*/
- window.onload = function(){
- var serverTime = parseInt($('dateWrapper').getAttribute('date'))*1000;
- SyncTimer.run(serverTime,function(date){
- var d = new Date(date);
- $('dateWrapper').innerHTML = d.format('yyyy-MM-dd hh:mm:ss');
- $('dateWrapper').setAttribute('date',parseInt(date/1000));
- });
- }
三、总结
- 总体实现还是比较麻烦,如果对时间精度要求不高可不必这么做。
- 还有一种情况未解决:用户从当前页面进入别的页面后修改客户端时间,之后后退到当前页面,此时时间计算不正确,但是暂时未找到解决方案。
- 此外发现两个有意思的东西:1. 在Firefox下如果将客户端时间改慢会导致setInterval停止运行,而setTimeout则不会;2. 在Chrome中,当用户修改了客户端时间后,setInterval中取到的Date的值并不会随用户的修改而修改。
相关推荐
客户端和服务端时间同步,实现了时间同步问题
通过注册表,实时本机同时作为时间服务器,或者客户端,实现时间同步。 实现平台,Windows2000,XP,Win7。可以与任何一台服务器相连,也可以自己也自己相连。需要完全C++代码的加QQ:182392810
比如,防止用户修改当前时间,造成数据逻辑混乱,客户端使用的时间应为服务器时间。 如何处理呢,我们可以使用Delphi Indy组件中的 TIdDayTimeServer和TIdDayTime一对组件。 TIdDayTimeServer用于服务器端,...
windows_2008_R2上建立NTP_Server和客户端实现时间同步
VC/C++源码,网络相关,时间同步 VC++源程序实现同步网络时间,基于C/S结构,启动服务端程序后,需要在客户端输入服务端的IP地址,然后点击“同步”按钮,如果顺利连接的话,则完成与服务器端的时间同步,注意,本...
计算机客户端使用NTP(Network Time Protocol)协议与这些时间服务器进行时间同步,使本机得到精确时间,本文简要描述了NTP协议的原理,对NTP协议的时间同步精度做了简要分析,并具体实现了SNTP(Simple Network Time ...
与远程时间同步服务器实现客户端,启动即可与时间服务器的同步,需要服务器作为主时间服务器,详见注册表文件
本软件为安装在服务器端的后台服务程序。用于客户端获取主机时间。客户端通过TCP/IP访问主机(端口:900),发送字符串:GETTIME,即可获得时间格式为:yyyy-MM-dd HH:mm:ss...这样就可轻松实现局域网内所有电脑时间同步。
输入服务器IP可实现系统时间同步,分为手动请求和自动请求两种,并将服务器端的应答存入txt文件。
服务端采用I/O复用模型(select函数)接收客户端的时间同步请求 服务端采用单线程,但要能同时接收多客户端的连接请 求,显示客户端IP和端口,并向其回送时间信息。 客户端尝试同时使用 UDP 和 TCP 来实现。 注:...
Roughenough Roughenough是Rust中的Roughtime安全时间同步客户端和服务器实现。 Roughenough的服务器和客户端是功能强大的Roughenough Roughenough是Rust中的Roughtime安全时间同步客户端和服务器实现。 Roughenough...
再加上我们的时间同步服务端的IP地址或者域名即可,其中prefer选项表示优先使用该时间同步服务器 #server 0.centos.pool.ntp.org iburst #server 1.centos.pool.ntp.org iburst #server 2.centos.pool.ntp.org ...
VC++源程序实现同步网络时间,基于C/S结构,启动服务端程序后,需要在客户端输入服务端的IP地址,然后点击“同步”按钮,如果顺利连接的话,则完成与服务器端的时间同步,注意,本程序并非是连接世界官方时间服务器...
1.基于流式套接字的时间同步服务器设计 2.基于流式套接字的服务器回射程序设计 (1)客户接收一行数据 (2)服务器定长接收数据 (3)客户服务器变长接收数据 3.实现并发服务器。要求并发服务器的实现采用创建...
这是一个简单,简单的python服务器和客户端实现,可将源目录中的文件从客户端同步到服务器上的目标目录。 总体上,通过粗略的测试和基本功能的实施,该项目花费了3 1/2小时才能完成。 当前,客户端和服务器可以...
C语言实现window(linux)平台的SNTP,本程序功能主要是实现电脑(或者设备)时间同步。摘录部分代码: unsigned char liVnMode; /* LeapSecond(2bits:0), VersionNumber(3bits: 3), Mode(3bits: Client=3, ...
通过邮件开通POP3和imap协议,可以在客户端实现读取邮件 标头、日期、附件等内容,并实现下载附件等功能。(测试网易、新浪、QQ免费邮箱可以实现删除邮件功能并同步到服务器邮箱,但测试免费试用期网易企业邮箱时,...
客户端连接后服务器能够发送一个欢迎消息给客户端,客户端发送time,发送当前时间(字符串形式)给客户端;客户端发送date,发送当前日期(字符串)给客户端;客户端输入quit,则退出程序 任务二:C#版同步通信程序...
在已知时间服务器IP的情况下,想在我的软件里嵌入一个NTP客户端校时功能,折腾了好一阵之后发现delphi7的indy控件包里有一个IDSNTP控件其实就能实现我想要的校时功能,在网上查找该控件的用法少之又少,indyDemo里也...
轮询,原理简单易懂,就是客户端通过一定的时间间隔以频繁请求的方式向服务器发送请求,来保持客户端和服务器端的数据同步。问题很明显,当客户端以固定频率向服务器端发送请求时,服务器端的数据可能并没有更新,...