客戶端實現正確的伺服器時間同步
一、問題描述
需要解決的問題很簡單,就是如何在頁面上比較準確的顯示伺服器時間。目前比較常用的方法就是根據基準時間使用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的值並不會隨使用者的修改而修改。
相關推薦
客戶端實現正確的伺服器時間同步
一、問題描述 需要解決的問題很簡單,就是如何在頁面上比較準確的顯示伺服器時間。目前比較常用的方法就是根據基準時間使用setTimeout或 setInterval來計算最新的時間,這樣的問題在於setTimeout與setInterval的時間精度比較低,經測試一分鐘大概能相差幾秒 (與電腦效能以及執行
關於客戶端與資料庫伺服器端的時間同步問題
這是一個做C/S的管理軟體開發時經常被忽略的問題,客戶端的時間與伺服器的時間如果有偏差,資料統計、報表等等肯定會有“意外”的情況發生。 意圖很簡單:從資料庫伺服器獲取到時間,根據這個時間修改當前客戶端電腦時間。 用Sql的函式getdate(),是比較容易的。 我們是基於do
收發檔案的伺服器端/客戶端實現
程式需求 客戶端接受使用者輸入的傳輸檔名 客戶端請求伺服器端傳輸該檔名所指檔案 伺服器端程式碼: #include <iostream> #include <string> #include <stdlib.h> #include
TCP 伺服器/客戶端(實現下載)
TCP/IP : TCP/IP:在網路通訊中,TCP/IP是主流協議() 應用層:使用者自定義的協議(HTTP,EMAIL,),用於使用者之間資料的傳送 &nbs
記筆記:C# Socket客戶端監聽伺服器端處理方案【同步】
方案主要功能: (1)客戶端同步監聽來自伺服器端的資料(開啟子執行緒監聽) (2)客戶端向伺服器端傳送資料(主執行緒傳送,並控制)
Android-使用者登陸的例子(伺服器端開發到客戶端實現)
1.回顧 上篇實現總結了App介面開發課程講解的內容 2.此篇 這篇將總結app介面開發裡的 使用者登入的例子! 3.基本步驟 (1)服務端實現 登陸介面 開發 (2)實現 json 和xml資料封裝類 (3)編寫 介面開
Xshell 5+lrzsz實現Windows客戶端向CentOS伺服器上傳檔案
安裝lrzsz 在centost的命令列上執行命令: yum install lrzsz 安裝完成後,可以進入需要上傳檔案的目錄,然後執行命令: rz 這時會彈出一個上傳檔案的視窗,選擇需要上傳的檔案即可上傳。
JAVA NIO 伺服器與客戶端實現示例(程式碼1)
公共類: [java] view plain copy print? package com.stevex.app.nio; import java.nio.ByteBuffer; import java.nio.CharBuffer; import j
網路程式設計——用執行緒實現可供多客戶端訪問的伺服器
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <string.h> #includ
android客戶端+JAVA WEB伺服器實現json資料解析
import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.But
網路程式設計——用程序實現可供多客戶端訪問的伺服器
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <string.h> #includ
JAVA NIO 伺服器與客戶端實現示例
公共類: package com.stevex.app.nio; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharacterCodingExcepti
Android客戶端與PC伺服器實現Socket通訊(wifi)
本文介紹Android終端持續掃描AP資訊併發送給伺服器端的實現。首先基於TCP協議在Android終端和PC兩端之間形成網路虛擬鏈路。使用ServerSocket建立TCP伺服器端,然後在Android客戶端使用Socket的構造器來連線伺服器。其中Android終端通過WIFI連線和PC處於同一區域網
Android客戶端+mysql+springmvc伺服器端實現登陸的小案例
首先是客戶端 通過輸入使用者名稱+密碼實現登入 點選登入後向伺服器傳送http請求 伺服器收到請求後驗證使用者名稱密碼是否與mysql資料庫上的相應欄位是否一致 然後返回json資料 客戶端獲取響應的結果 然後提醒是否登入成功 MainActivity程式碼: public
騰訊雲Linux伺服器搭建(九) Linux上DB2的客戶端實現資料的備份和恢復
問題:DB2伺服器裝在AIX上,不允許直接利用伺服器做資料的匯出匯入。但是由於專案需要,需要頻繁的對一些表進行資料的備份和恢復。所以只能在一個其他伺服器上安裝DB2客戶端,通過客戶端來實現資料的備份和恢復。 思路:先從IBM官網下載客戶端;然後安裝到一個應用伺服器上(Cen
Java NIO 伺服器與客戶端實現檔案下載
import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.chann
Javascript實現商品秒殺倒計時(時間與伺服器時間同步)
現在有很多網站都在做秒殺商品,而這其中有一個很重要的環節就是倒計時。 關於倒計時,有下面幾點需要注意: 1.應該使用伺服器時間而不是本地時間(本地時間存在時區不同、使用者自行設定等問題)。 2.要考慮網路傳輸的耗時。 3.獲取時間時可直接從AJAX的響應頭中讀取(通過get
實現Android客戶端與Eclipse伺服器端基於Okthhp簡單通訊
最近在重溫知識,所以藉此機會也想把自己寫的一些心得寫出來供大家分享,寫的有誤或者不好的地方望大家見諒,好了,廢話少說,直接進入正題,下面給大家介紹的就是基於目前主流網路通訊框架的okhttp實現的Android與Eclipse通訊。首先說明:我用的Android客戶端是And
Android客戶端與Tomcat伺服器通訊實現登入驗證
一.功能描述 在Android客戶端實現登入介面,當將使用者名稱和密碼填入文字框並點選登入按鈕時,將認證資訊傳送至Tomcat伺服器進行認證,若使用者名稱和密碼匹配,則Android客戶端提示登入成功,否則提示登陸失敗。 二.開發環境 Android客戶端:Andro
Javascript實現秒殺倒計時(時間與伺服器時間同步)
現在有很多網站都在做秒殺商品,而這其中有一個很重要的環節就是倒計時。 關於倒計時,有下面幾點需要注意: 1.應該使用伺服器時間而不是本地時間(本地時間存在時區不同、使用者自行設定等問題)。 2.要考慮網路傳輸的耗時。 3.獲取時間時可直接從AJAX的響應頭中讀取(通過getResponseHeader('D