同步客户端javascript时钟和服务器日期的最佳方法

np8igboo  于 2022-12-28  发布在  Java
关注(0)|答案(9)|浏览(120)

我有一个显示数字时钟的任务(精确到分钟)在某个固定时区的HTML页面上(MSK或MSD -取决于当前日期)。我想避免依赖于客户端系统时钟,所以需要与服务器同步。HTTP服务器在每个响应中发送日期报头,这样我们就可以发送 AJAX GET或HEAD请求到我们网站的任何URL来获取服务器日期,计算与客户端日期的差值,并在使用setTimeout()更新时钟时使用它。仍存在其他问题:时区切换为日光设置,延迟占非常缓慢的连接。
有什么办法能最简单地解决这个问题吗?我更喜欢不用服务器端编程就能解决它。

j2cgzkjk

j2cgzkjk1#

您可以在代码中使用NTP (Network Time Protocol)计算精确时间,
我试着给你解释一下:
1.我们有发送请求的客户端时间(例如4/3/2012 13:56:10.123)
1.将ClientTime发送到服务器
1.我们为请求提供了往返时间,我称之为RequestTime(例如:需要5秒)
1.在服务器中,我们计算服务器和客户端之间的时间差(例如:如果ServerTime - ClientTime = ServerClientDifferenceTimeWithRequestTime),您现在应该在步骤3中将此差值包括往返请求时间,然后应该从差值中删除往返时间
1.服务器发送包括服务器客户端差异时间与请求时间和服务器时间的响应
1.我们有往返时间用于响应,我称之为ResponseTime(例如:需要3秒钟)
1.在客户端,我们再次计算服务器和客户端之间的时间差(例如:如果服务器时间-客户端时间=服务器客户端差分时间与响应时间),则再次:现在,您应该在步骤6中计算包括往返响应时间在内的差值
1.我们在客户端中有Now time
1.你应该计算简单的方程在客户端:

X(同步时间)= Now + (ServerClientDifferenceTimeWithRequestTime - RquestTime)
X(同步时间)= Now + (ServerClientDifferenceTimeWithResponseTime - ResponseTime)

Now - ClientTime = RquestTime + ResponseTime =〉
Now - (ServerClientDiffRq - RquestTime) = Now - (ServerClientDiffRs - ResponseTime)
如果你解决它,你会发现:
ResponseTime = (ServerClientDifferenceTimeWithRequestTime - Now + ClientTime + - ServerClientDifferenceTimeWithResponseTime )/2
然后您可以使用以下公式在客户端找到同步时间或服务器时间:

X(同步时间)= Now + (ServerClientDifferenceTimeWithResponseTime - ResponseTime)

我展示了简单的代码,但是当你想写它的时候,不要忘记使用UTC日期和时间函数...

服务器端(例如php、c#):
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#:

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+"}");

客户端(带有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);
});
xmjla07d

xmjla07d2#

这两个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;
}

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

calcOffset()计算与服务器时间的偏移量,并补偿GMT/UTC。
getServerTime()获取本地时间偏移量以匹配服务器,使用本地时区。
如果calcOffset()需要沿着时间来执行,你可能会失去一些秒精度。也许执行时间可以考虑在内...
如果您担心本地时间或服务器时间更改为夏令时或从夏令时更改为夏令时时时计算的偏移量会出错,您可以在每个时钟小时后重新计算一点,系统将补偿夏令时的更改。可能需要等到本地和服务器时钟都过了小时。
这个例子只适用于IE,因为我认为“Msxml2.XMLHTTP”......

h5qlskok

h5qlskok3#

我发现上面的@mehdi-yeganeh算法并没有给我带来有用的结果,但这个想法是合理的:使用NTP算法(或至少是它的弱版本)来同步服务器和客户端时钟。
这是我的最终实现,它使用服务器响应头,如果有额外的准确性(请纠正我,如果我错了,我自己的测试说,这是相当准确的)。

浏览器端(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)));
    }
});

服务器端(php,但可以是任何内容):

路由'GET /ntp'上的服务器应返回如下内容:

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

如果您的PHP〉5.4,那么您可以保存对microtime()的调用,并使用以下命令使其更精确:

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

注解

这种方式可能被视为一种贫民窟,有一些其他的堆栈溢出的答案,可以指导你走向一个更好的解决方案:

cetgtptt

cetgtptt4#

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

5tmbdcev

5tmbdcev5#

如果你只要求精确到分钟,我只会每隔30秒左右向服务器请求一次更新。根本不要依赖客户端的时间,而是使用他们的系统时钟来保持更新之间的时钟准确。我想你已经回答了自己的问题。
如果我们能更好地理解你的真正意图会有帮助。
如果你只是想让一个时钟显示服务器上的时间,然后调整到某个时区,那么就在客户端使用偏移量来做。通过使用从服务器接收的日期来处理适用时区中的DST。如果你想确定延迟,你可能需要在服务器上使用一个小脚本来计算差异。但是如上所述,这将有助于更好地理解这个问题。2如果精确到分钟,那么延迟就显得不那么重要了。

wixjitnu

wixjitnu6#

感谢@Mehdi Yeganeh和@Fedearne。我实现了我的函数,既使用了逻辑又使用了它的工作。
https://gist.github.com/ethaizone/6abb1d437dbe406fbed6

0aydgbwb

0aydgbwb7#

有点太晚了,但希望这可能会帮助一些人!
我有一个类似的要求,即显示服务器时钟而不考虑客户端的计算机。因此,基本上,您只需使用以下三个参数:

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.

然后进行以下计算:

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.

下面是运行中的完整代码:

<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>
62lalag4

62lalag48#

如果我理解正确的话,我们只有3个值
1.发送请求时的客户端时间
1.服务器时间
1.客户端收到响应的时间
如果假设请求和响应时间相等,我们可以通过下式计算服务器和客户端之间时差:

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

并在客户端上获得正确的时间

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

qpgpyjmq9#

我会同步时间在初始化与互联网时间服务器。
http://tf.nist.gov/service/its.htm

相关问题