客户端与服务器端时间保持一致

2024-05-14 22:58

本文主要是介绍客户端与服务器端时间保持一致,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

客户端实现准确的服务器时间同步 ,这篇博文是转过来的,原博文地址为:http://blog.csdn.net/chelen_jak/article/details/35335785,运气的缘故在我最需要秒杀倒计时时间同步问题时看到这篇博文,我首先说下我之前的思路,我使用setinterval来进行倒计时的时间,当然从服务器传过来一个时间戳,然后前台利用这个时间戳进行倒计时,当时傻傻的以为这就是与服务器同步,但是深入的想了下,这个想法非常的错误,这样少了一个至关重要的一点,传输时所消耗的时间是个重要的问题,而且并不能实时的与服务器交互,又不能完全使用本地时间,因为还是会出现差异。这样给服务器的压力太大,于是上网仔细查看了下,发现使用setinterval这个的话非常的不靠谱,转载的这篇博文也有说具体是为什么,所以说网上的很多所谓的倒计时代码都是并不能用。但是换句话,由于我做的是秒杀系统,对时间的要求非常高,所以要这样做,如果说你要做的没这么精确,所以并不需要这么的去做。
一、问题描述

需要解决的问题很简单,就是如何在页面上比较准确的显示服务器时间。目前比较常用的方法就是根据基准时间使用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的值并不会随用户的修改而修改。
  • 下面写上我修改后的代码,可以直接粘贴使用的倒计时代码,而不是系统时间的代码
  • <script>/*定义*/    var SyncTimer = (function(){    /*跨页面数据存储器*/    //存储最近一次的客户端时间,用于在页面前进、后退时进行时间矫正    var memoryElementID = 'sync_timer_memory_el';    //存储矫正后的最新基准时间,当页面前进、后退到当前页面时会以此值为新的基准时间    var memoryBaseTimeElementID = 'sync_timer_memory_base_time_el';    document.write('<input type="hidden" id="' + memoryElementID + '">');    document.write('<input type="hidden" 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({$time})*1000;    SyncTimer.run(serverTime,function(date){var intDiff = (1437364800+18000)*1000 - date;intDiff =  Math.floor(intDiff / 1000);var day=0,hour=0,minute=0,second=0;//时间默认值		if(intDiff > 0){day = Math.floor(intDiff / (60 * 60 * 24));hour = Math.floor(intDiff / (60 * 60)) - (day * 24);minute = Math.floor(intDiff / 60) - (day * 24 * 60) - (hour * 60);second = Math.floor(intDiff) - (day * 24 * 60 * 60) - (hour * 60 * 60) - (minute * 60);}if (minute <= 9) minute = '0' + minute;if (second <= 9) second = '0' + second;-$('#day_show').html(day+"天");$('#hour_show').html('<s id="h"></s>'+hour+'时');$('#minute_show').html('<s></s>'+minute+'分');$('#second_show').html('<s></s>'+second+'秒');if(minute == '00' && second == '00' && hour == '0' && day == '0'){temp = window.clearInterval(temp);$('#now').removeClass('btn-danger');$('#now').addClass('btn-success');}});    }    </script>


这篇关于客户端与服务器端时间保持一致的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/990095

相关文章

如何利用Java获取当天的开始和结束时间

《如何利用Java获取当天的开始和结束时间》:本文主要介绍如何使用Java8的LocalDate和LocalDateTime类获取指定日期的开始和结束时间,展示了如何通过这些类进行日期和时间的处... 目录前言1. Java日期时间API概述2. 获取当天的开始和结束时间代码解析运行结果3. 总结前言在J

修改若依框架Token的过期时间问题

《修改若依框架Token的过期时间问题》本文介绍了如何修改若依框架中Token的过期时间,通过修改`application.yml`文件中的配置来实现,默认单位为分钟,希望此经验对大家有所帮助,也欢迎... 目录修改若依框架Token的过期时间修改Token的过期时间关闭Token的过期时js间总结修改若依

Go Mongox轻松实现MongoDB的时间字段自动填充

《GoMongox轻松实现MongoDB的时间字段自动填充》这篇文章主要为大家详细介绍了Go语言如何使用mongox库,在插入和更新数据时自动填充时间字段,从而提升开发效率并减少重复代码,需要的可以... 目录前言时间字段填充规则Mongox 的安装使用 Mongox 进行插入操作使用 Mongox 进行更

使用Java实现获取客户端IP地址

《使用Java实现获取客户端IP地址》这篇文章主要为大家详细介绍了如何使用Java实现获取客户端IP地址,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 首先是获取 IP,直接上代码import org.springframework.web.context.request.Requ

对postgresql日期和时间的比较

《对postgresql日期和时间的比较》文章介绍了在数据库中处理日期和时间类型时的一些注意事项,包括如何将字符串转换为日期或时间类型,以及在比较时自动转换的情况,作者建议在使用数据库时,根据具体情况... 目录PostgreSQL日期和时间比较DB里保存到时分秒,需要和年月日比较db里存储date或者ti

Python手搓邮件发送客户端

《Python手搓邮件发送客户端》这篇文章主要为大家详细介绍了如何使用Python手搓邮件发送客户端,支持发送邮件,附件,定时发送以及个性化邮件正文,感兴趣的可以了解下... 目录1. 简介2.主要功能2.1.邮件发送功能2.2.个性签名功能2.3.定时发送功能2. 4.附件管理2.5.配置加载功能2.6.

Python 标准库time时间的访问和转换问题小结

《Python标准库time时间的访问和转换问题小结》time模块为Python提供了处理时间和日期的多种功能,适用于多种与时间相关的场景,包括获取当前时间、格式化时间、暂停程序执行、计算程序运行时... 目录模块介绍使用场景主要类主要函数 - time()- sleep()- localtime()- g

Redis连接失败:客户端IP不在白名单中的问题分析与解决方案

《Redis连接失败:客户端IP不在白名单中的问题分析与解决方案》在现代分布式系统中,Redis作为一种高性能的内存数据库,被广泛应用于缓存、消息队列、会话存储等场景,然而,在实际使用过程中,我们可能... 目录一、问题背景二、错误分析1. 错误信息解读2. 根本原因三、解决方案1. 将客户端IP添加到Re

如何用Java结合经纬度位置计算目标点的日出日落时间详解

《如何用Java结合经纬度位置计算目标点的日出日落时间详解》这篇文章主详细讲解了如何基于目标点的经纬度计算日出日落时间,提供了在线API和Java库两种计算方法,并通过实际案例展示了其应用,需要的朋友... 目录前言一、应用示例1、天安门升旗时间2、湖南省日出日落信息二、Java日出日落计算1、在线API2

如何使用 Bash 脚本中的time命令来统计命令执行时间(中英双语)

《如何使用Bash脚本中的time命令来统计命令执行时间(中英双语)》本文介绍了如何在Bash脚本中使用`time`命令来测量命令执行时间,包括`real`、`user`和`sys`三个时间指标,... 使用 Bash 脚本中的 time 命令来统计命令执行时间在日常的开发和运维过程中,性能监控和优化是不