简体   繁体   English

将客户端 javascript 时钟与服务器日期同步的最佳方式

[英]The best way to synchronize client-side javascript clock with server date

I have a task to show digital clock (with minutes precision) on HTML page in some fixed timezone (MSK or MSD - depending on current date).我有一项任务是在某个固定时区(MSK 或 MSD - 取决于当前日期)的 HTML 页面上显示数字时钟(分钟精度)。 I'd like to avoid relying on client system clock, so some synchronization with server is required.我想避免依赖客户端系统时钟,因此需要与服务器进行一些同步。 HTTP server sends Date header in each response so we can send an AJAX GET or HEAD request to any URL of our site to get server date, calculate the difference with client date and use it when updating clock with setTimeout(). HTTP 服务器在每个响应中发送 Date 标头,因此我们可以向我们站点的任何 URL 发送 AJAX GET 或 HEAD 请求以获取服务器日期,计算与客户端日期的差异并在使用 setTimeout() 更新时钟时使用它。 There are other issues remains: timezone switching for daylight settings, latency accounting for very slow connections.还有其他问题仍然存在:日光设置的时区切换,连接速度非常慢的延迟。

Any idea to this task the simpliest way?对这个任务有最简单的想法吗? I'd prefer to solve it without server-side programming.我更愿意在没有服务器端编程的情况下解决它。

You can calculate exact time with NTP (Network Time Protocol) in your codes,您可以在代码中使用NTP(网络时间协议)计算准确时间,

i try to explain for you:我试着为你解释:

  1. We have ClientTime on Sending Request (for example 4/3/2012 13:56:10.123)我们在发送请求时有 ClientTime(例如 4/3/2012 13:56:10.123)
  2. You send ClientTime to Server您将 ClientTime 发送到服务器
  3. We have Round-trip time for request, i called it RequestTime (for example: It takes 5 seconds)我们有请求的往返时间,我称之为RequestTime (例如:需要5秒)
  4. In Server, We calculate Difference time between Server and Client (for example: It ServerTime - ClientTime = ServerClientDifferenceTimeWithRequestTime), you should now this Difference including Round-trip request time in step 3 then you should remove round trip time from Difference在服务器中,我们计算服务器和客户端之间的差异时间(例如:它 ServerTime - ClientTime = ServerClientDifferenceTimeWithRequestTime),您现在应该在第 3 步中将此差异包括往返请求时间,然后您应该从差异中删除往返时间
  5. Server Send response that include ServerClientDifferenceTimeWithRequestTime and ServerTime服务器发送响应,包括 ServerClientDifferenceTimeWithRequestTime 和 ServerTime
  6. We have Round-trip time for response, i called it ResponseTime (for example: It takes 3 seconds)我们有响应的往返时间,我称之为响应时间(例如:需要 3 秒)
  7. In client, We calculate Difference time between Server and Client again (for example: It ServerTime - ClientTime = ServerClientDifferenceTimeWithResponseTime), again: you should now this Difference including Round-trip response time in step 6在客户端,我们再次计算 Server 和 Client 之间的时差(例如:It ServerTime - ClientTime = ServerClientDifferenceTimeWithResponseTime),再次:您现在应该在步骤 6 中计算包括往返响应时间在内的差值
  8. We have Now time in Client我们现在有时间在客户端
  9. You should Calculate simple equations in client:您应该在客户端计算简单的方程:

X(SyncedTime) = Now + (ServerClientDifferenceTimeWithRequestTime - RquestTime) X(SyncedTime) = Now + (ServerClientDifferenceTimeWithRequestTime - RquestTime)

X(SyncedTime) = Now + (ServerClientDifferenceTimeWithResponseTime - ResponseTime) X(SyncedTime) = Now + (ServerClientDifferenceTimeWithResponseTime - ResponseTime)

Now - ClientTime = RquestTime + ResponseTime => Now - ClientTime = RquestTime + ResponseTime =>

Now - (ServerClientDiffRq - RquestTime) = Now - (ServerClientDiffRs - ResponseTime)

if you solve it you found this:如果你解决了它,你会发现这个:

ResponseTime = (ServerClientDifferenceTimeWithRequestTime - Now + ClientTime + - ServerClientDifferenceTimeWithResponseTime )/2

and then you can found synced time or server time in client with this equation:然后您可以使用以下等式在客户端中找到同步时间或服务器时间:

X(SyncedTime) = Now + (ServerClientDifferenceTimeWithResponseTime - ResponseTime) X(SyncedTime) = Now + (ServerClientDifferenceTimeWithResponseTime - ResponseTime)

I show simple code but when you want write it don`t forgot use UTC date & time functions...我展示了简单的代码,但是当你想写它时不要忘记使用 UTC 日期和时间函数......

Server Side (for example php, c#):服务器端(例如 php、c#):

PHP: PHP:

header('Content-Type: application/json; charset=utf-8');
$clientTime = $_GET["ct"] * 1; //for php 5.2.1 or up: (float)$_GET["ct"];
$serverTimestamp = round(microtime(true)*1000); // (new DateTime())->getTimestamp();
$serverClientRequestDiffTime = $serverTimestamp - $clientTime;
echo "{\"diff\":$serverClientRequestDiffTime,\"serverTimestamp\":$serverTimestamp}";

C#: C#:

long clientTime = long.Parse(Request.Form["ct"]);
long serverTimestamp = (DateTime.Now.Ticks-(new DateTime(1970,1,1) - DateTime.MinValue).Ticks) / 10000;
long serverClientRequestDiffTime = serverTimestamp - clientTime;
Response.Write("{\"diff\":"+serverClientRequestDiffTime+",\"serverTimestamp\":"+serverTimestamp+"}");

Client Side (Javascript with Jquery ):客户端(带有Jquery 的Javascript):

var clientTimestamp = (new Date()).valueOf();
$.getJSON('http://yourhost.com/getdatetimejson/?ct='+clientTimestamp, function( data ) {
    var nowTimeStamp = (new Date()).valueOf();
    var serverClientRequestDiffTime = data.diff;
    var serverTimestamp = data.serverTimestamp;
    var serverClientResponseDiffTime = nowTimeStamp - serverTimestamp;
    var responseTime = (serverClientRequestDiffTime - nowTimeStamp + clientTimestamp - serverClientResponseDiffTime )/2

    var syncedServerTime = new Date((new Date()).valueOf() + (serverClientResponseDiffTime - responseTime));
    alert(syncedServerTime);
});

These two Javascript functions should do the trick for you.这两个 Javascript 函数应该可以为您解决问题。

var offset = 0;
function calcOffset() {
    var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
    xmlhttp.open("GET", "http://stackoverflow.com/", false);
    xmlhttp.send();

    var dateStr = xmlhttp.getResponseHeader('Date');
    var serverTimeMillisGMT = Date.parse(new Date(Date.parse(dateStr)).toUTCString());
    var localMillisUTC = Date.parse(new Date().toUTCString());

    offset = serverTimeMillisGMT -  localMillisUTC;
}

function getServerTime() {
    var date = new Date();

    date.setTime(date.getTime() + offset);

    return date;
}

EDIT: removed ".replace(/^(. )[\\s\\S] /,"$1")".编辑:删除了“.replace(/^(. )[\\s\\S] /,”$1”)”。

calcOffset() calculates the offset from server time and compensates for GMT/UTC. calcOffset() 计算与服务器时间的偏移量并补偿 GMT/UTC。

getServerTime() to get the local time offset to match the servers, using the local timezone. getServerTime() 使用本地时区获取本地时间偏移量以匹配服务器。

If calcOffset() takes along time to execute you might loose some seconds precision.如果 calcOffset() 需要很长时间来执行,您可能会失去几秒钟的精度。 Maybe the execution time could be taken into account....也许可以考虑执行时间......

If you are worried about the calculated offset becoming wrong when either local time or server time change to or from daylight savings time you could recalculate a litle after every clock-hour, the system will compensate for changes in dayligt savings time.如果您担心本地时间或服务器时间更改为夏令时或从夏令时更改时计算的偏移量会出错,您可以在每个时钟小时后重新计算一点,系统将补偿夏令时的变化。 It might be necessary to wait until both the local and server clock has passed the hour.可能需要等到本地时钟和服务器时钟都过去了一个小时。

The example only works in IE because of "Msxml2.XMLHTTP" i think.....该示例仅适用于 IE,因为我认为“Msxml2.XMLHTTP”.....

I've found that the algorithm of @mehdi-yeganeh above didn't give me useful results but the idea is sound: to use the NTP algorithm (or at least a weak version of it) to synchronize the server and client clocks.我发现上面@mehdi-yeganeh 的算法没有给我有用的结果,但这个想法是合理的:使用 NTP 算法(或至少是它的弱版本)来同步服务器和客户端时钟。

This is my final implementation, it uses the server response headers if available for extra accuracy (please correct me if I'm wrong, my own tests say this is quite accurate).这是我的最终实现,它使用服务器响应标头(如果可用)以提高准确性(如果我错了,请纠正我,我自己的测试表明这是非常准确的)。

browser-side (javascript):浏览器端(javascript):

// the NTP algorithm
// t0 is the client's timestamp of the request packet transmission,
// t1 is the server's timestamp of the request packet reception,
// t2 is the server's timestamp of the response packet transmission and
// t3 is the client's timestamp of the response packet reception.
function ntp(t0, t1, t2, t3) {
    return {
        roundtripdelay: (t3 - t0) - (t2 - t1),
        offset: ((t1 - t0) + (t2 - t3)) / 2
    };
}

// calculate the difference in seconds between the client and server clocks, use
// the NTP algorithm, see: http://en.wikipedia.org/wiki/Network_Time_Protocol#Clock_synchronization_algorithm
var t0 = (new Date()).valueOf();

$.ajax({
    url: '/ntp',
    success: function(servertime, text, resp) {
        // NOTE: t2 isn't entirely accurate because we're assuming that the server spends 0ms on processing.
        // (t1 isn't accurate either, as there's bound to have been some processing before that, but we can't avoid that)
        var t1 = servertime,
            t2 = servertime,
            t3 = (new Date()).valueOf();

        // we can get a more accurate version of t2 if the server's response
        // contains a Date header, which it generally will.
        // EDIT: as @Ariel rightly notes, the HTTP Date header only has 
        // second resolution, thus using it will actually make the calculated
        // result worse. For higher accuracy, one would thus have to 
        // return an extra header with a higher-resolution time. This 
        // could be done with nginx for example:
        // http://nginx.org/en/docs/http/ngx_http_core_module.html
        // var date = resp.getResponseHeader("Date");
        // if (date) {
        //     t2 = (new Date(date)).valueOf();
        // }

        var c = ntp(t0, t1, t2, t3);

        // log the calculated value rtt and time driff so we can manually verify if they make sense
        console.log("NTP delay:", c.roundtripdelay, "NTP offset:", c.offset, "corrected: ", (new Date(t3 + c.offset)));
    }
});

server-side (php, but could be anything):服务器端(php,但可以是任何东西):

Your server at route 'GET /ntp' should return something like:您在路由 'GET /ntp' 上的服务器应该返回如下内容:

echo (string) round(microtime(true) * 1000);

If you have PHP >5.4, then you can save a call to microtime() and make it a bit more accurate with:如果您的 PHP > 5.4,那么您可以保存对 microtime() 的调用并使其更准确:

echo (string) round($_SERVER['REQUEST_TIME_FLOAT'] * 1000);

NOTE笔记

This way might be seen as kind of ghetto, there are some other Stack Overflow answers that could guide you towards a better solution:这种方式可能被视为一种贫民窟,还有一些其他 Stack Overflow 答案可以指导您找到更好的解决方案:

如果您打算使用 ajax,您应该记住 readyState==2 和 readyState==3 之间的客户端时间,因为服务器时间将设置在收到请求和准备响应之间的某个时间

I'd only request the update from the server every 30s or so, if you require precision only to the minute.如果您只需要精确到分钟,我只会每 30 秒左右从服务器请求更新一次。 Don't rely on the client time at all, but use their system clock to keep the clock accurate between updates.不要完全依赖客户端时间,而是使用他们的系统时钟来保持更新之间的时钟准确。 I think you answered your own question?我想你回答了你自己的问题?

It would help if we better understood what you're actually trying to do.如果我们更好地理解您实际尝试做什么,将会有所帮助。

If you simply want a clock to display the time on the server, which is then adjusted to a certain timezone, do it clientside with offsets.如果您只是想要一个时钟在服务器上显示时间,然后将其调整到某个时区,请在客户端使用偏移量。 Handle DST in the timezones it is applicable by using the date you receive from the server as well.通过使用您从服务器收到的日期,在适用的时区处理 DST。 If you want to determine latency, you would probably need a small script on the server to calculated the difference.如果您想确定延迟,您可能需要服务器上的一个小脚本来计算差异。 But as above, it would help to understand the problem better.但如上所述,这将有助于更好地理解问题。 If precision is only to the minute, latency seems less critical.如果精确到分钟,延迟似乎就不那么重要了。

Thanks to @Mehdi Yeganeh and @Fedearne.感谢@Mehdi Yeganeh 和@Fedearne。 I implement my function to use both logic and it's work.我实现了我的功能以使用这两种逻辑并且它的工作原理。

https://gist.github.com/ethaizone/6abb1d437dbe406fbed6 https://gist.github.com/ethaizone/6abb1d437dbe406fbed6

Little too late but hope this might help someone!太晚了,但希望这可以帮助某人!

I had a similar requirement to display a server clock irrespective of client's machine.无论客户端的机器如何,我都有类似的要求来显示服务器时钟。 So basically, you just play with three parameters here:所以基本上,你只需在这里使用三个参数:

x = clientReqTimestamp = (new Date()).valueOf();  //Client Timestamp at request.
y = serverTimestamp;  //query your server for Unix Timestamp.
z = clientRespTimestamp = (new Date()).valueOf();  //Client Timestamp on receiving response.

Then do the below calculation:然后进行以下计算:

var reqElapsed = Math.abs(y - x);  //time taken in milliseconds to hit the server
var respElapsed = Math.abs(z - y);  //time taken in milliseconds to get response from server
var serverNewTime = z + respElapsed;  // Voila! actual server time.

Below is the full code in action:下面是运行中的完整代码:

<script>
       
    var clientTimestamp = (new Date()).valueOf();
    
    var Data = {
        OperatorMobileNo: 'XXXXXXXXXX',
        requestClientTime: clientTimestamp
    };
    $.ajax({
        type: "POST",
        url: serviceURLx + "/XXXX/GetServerDateTime/1.0",
        dataType: "JSON",
        data: JSON.stringify(Data),
        contentType: "application/json; charset=utf-8",
        success: function (responseData) {
            debugger;
            var responseJSON = JSON.parse(JSON.stringify(responseData));
            if (responseJSON.ResponseCode === "000") {
    
                var x = clientReqTimestamp = clientTimestamp;
                    // If server time is in seconds => multiply by 1000 to convert sec to milli
                var y = serverTimestamp = responseJSON.Response.ServTimestamp * 1000;
                var z = clientRespTimestamp = (new Date()).valueOf();
                
                var reqElapsed = Math.abs(y - x);
                var respElapsed = Math.abs(z - y);

                var serverNewTime = z + respElapsed;
                
                debugger;
                //Init Server Clock
                setInterval( function() {
                    debugger;
                    var servClockT = new Date(serverNewTime += 1000);
                    document.getElementById('serverClock').innerHTML = servClockT;
                }, 1000);
            }
            else {
                swal("", "Unable To Fetch Server Time!", "info");
                console.log(responseJSON.ResponseCode);
            }
        },
        error: function () {
        }
    });
</script>

If I understand question correctly we have only 3 values如果我正确理解问题,我们只有 3 个值

  1. clientTimeWhenRequestSent客户端发送请求时间
  2. serverTime服务器时间
  3. clientTimeWhenResponseReceived客户端接收响应时间

if assume request and response time are equal we can calculate timeDifference between server and client by:如果假设请求和响应时间相等,我们可以通过以下方式计算服务器和客户端之间的 timeDifference:

const diff = serverTime - clientTimeWhenRequestSent 
- (clientTimeWhenResponseReceived - clientTimeWhenRequestSent)/2;

and get correct time on client with help of并在客户的帮助下获得正确的时间

const correctClienTime =  (new Date()).valueOf() + diff;

I would sync the time during initialization with Internet Time Server.我会在初始化期间与 Internet 时间服务器同步时间。

http://tf.nist.gov/service/its.htm http://tf.nist.gov/service/its.htm

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在客户端生成唯一 ID 的最佳方法(使用 Javascript) - Best way to generate unique ids client-side (with Javascript) 使用 Javascript 客户端计算文本区域中超链接的最佳方法? - Best way to count hyperlinks in textarea using Javascript client-side? 服务器端和客户端JavaScript - Server-side and client-side JavaScript 在用户控件中将服务器端数据获取到客户端脚本的最佳方法 - Best way to get server-side data to client-side script in usercontrol 客户端Javascript服务器 - 可能吗? - Client-side Javascript server - possible? 在客户端 javascript 或 websocket 服务器上转义? - Escape in client-side javascript or on websocket server? 有没有一种方法可以同步服务器和客户端的验证过程? - Is there a way to synchronize validation process of server and client side? 在客户端将服务器端变量传递给 JavaScript 的最佳方法是什么? - What is the best way to pass server side variables to JavaScript on the client side? 是否有一种简单的方法可以让客户端JavaScript访问像Enums这样的服务器端类? - Is there a simple way to give client-side JavaScript access to server-side classes like Enums? 在 Eleventy 中执行小型客户端 JavaScript 的最佳方法是什么? - What's the best way to perform small client-side JavaScript in Eleventy?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM