javascript 在javascript中以良好的精度(> 0.5s)同步时间(类似NTP)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8478179/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 03:34:51  来源:igfitidea点击:

Synchronize time in javascript with a good precision (>0.5s) (NTP-like)

javascriptajaxtimentp

提问by micred

I'm looking for a way to synchronize time between clients with a good precision (let's say 0.5 seconds at least).

我正在寻找一种方法来以良好的精度同步客户端之间的时间(假设至少为 0.5 秒)。

I exclude using jsontime or exploiting timestamp in server response headers due to a poor precision (a second or maybe less).

由于精度较差(一秒或更短),我排除在服务器响应标头中使用 jsontime 或利用时间戳。

UPDATE: It should work even with mobile connections. It's not unfrequent (e.g. here in Italy) that 3G connections itself have a round trip time around 0.5s, so algorithm has to be robust.

更新:即使使用移动连接,它也应该可以工作。3G 连接本身的往返时间约为 0.5 秒,这并不少见(例如在意大利),因此算法必须稳健。

回答by Aadit M Shah

Resort to the good old ICMP Timestampmessage scheme. It's fairly trivial to implement in JavaScript and PHP.

求助于旧的ICMP 时间戳消息方案。在 JavaScript 和 PHP 中实现相当简单。

Here's an implementation of this scheme using JavaScript and PHP:

这是使用 JavaScript 和 PHP 实现的该方案:

// browser.js

var request = new XMLHttpRequest();
request.onreadystatechange = readystatechangehandler;
request.open("POST", "http://www.example.com/sync.php", true);
request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
request.send("original=" + (new Date).getTime());

function readystatechangehandler() {
    var returned = (new Date).getTime();
    if (request.readyState === 4 && request.status === 200) {
        var timestamp = request.responseText.split('|');
        var original = + timestamp[0];
        var receive = + timestamp[1];
        var transmit = + timestamp[2];
        var sending = receive - original;
        var receiving = returned - transmit;
        var roundtrip = sending + receiving;
        var oneway = roundtrip / 2;
        var difference = sending - oneway; // this is what you want
        // so the server time will be client time + difference
    }
}

Now for the sync.phpcode:

现在的sync.php代码:

<?php
    $receive = round(microtime(true) * 1000);
    echo $_POST["original"] . '|';
    echo $receive . '|';
    echo round(microtime(true) * 1000);
?>

I haven't tested the above code, but it should work.

我还没有测试过上面的代码,但它应该可以工作。

Note:The following method will accurately calculate the time difference between the client and the server provided that actual time to send and receive messages is the same or approximately the same. Consider the following scenario:

注意:以下方法将准确计算客户端和服务器之间的时间差,前提是实际发送和接收消息的时间相同或大致相同。考虑以下场景:

  Time    Client   Server
-------- -------- --------
Original        0        2
Receive         3        5
Transmit        4        6
Returned        7        9
  1. As you can see, the client and server clocks are 2 units off sync. Hence when the client sends the timestamp request, it records the original time as 0.
  2. The server receives the request 3 units later, but records the receive time as 5 units because it's 2 units ahead.
  3. Then it transmits the timestamp reply one unit later and records the transmit time as 6 units.
  4. The client receives the reply after 3 units (i.e. at 9 units according to the server). However, since it's 2 units behind the server it records the returned time as 7 units.
  1. 如您所见,客户端和服务器时钟有 2 个不同步单位。因此,当客户端发送时间戳请求时,它会将原始时间记录为 0。
  2. 服务器在 3 个单位后收到请求,但将接收时间记录为 5 个单位,因为它提前了 2 个单位。
  3. 然后它在一个单位后传输时间戳回复,并将传输时间记录为 6 个单位。
  4. 客户端在 3 个单位后收到回复(即根据服务器在 9 个单位)。但是,由于它在服务器后面 2 个单位,因此它将返回的时间记录为 7 个单位。

Using this data, we can calculate:

使用这些数据,我们可以计算:

Sending = Receive - Original = 5 - 0 = 5
Receiving = Returned - Transmit = 7 - 6 = 1
Roundtrip = Sending + Receiving = 5 + 1 = 6

As you can see from above, the sending and receiving times are calculated incorrectly depending upon how much the client and server are off sync. However, the roundtrip time will always be correct because we are first adding two units (receive + original), and then subtracting two units (returned - transmit).

从上面可以看出,发送和接收时间的计算不正确,具体取决于客户端和服务器不同步的程度。但是,往返时间总是正确的,因为我们首先添加两个单位(接收 + 原始),然后减去两个单位(返回 - 发送)。

If we assume that the oneway time is always half of the roundtrip time (i.e. the time to transmit is the time to receive, then we can easily calculate the time difference as follows):

如果我们假设单程时间总是往返时间的一半(即发送时间是接收时间,那么我们可以很容易地计算出时间差如下):

Oneway = Roundtrip / 2 = 6 / 2 = 3
Difference = Sending - Oneway = 5 - 3 = 2

As you can see, we accurately calculated the time difference as 2 units. The equation for time difference is always sending - onewaytime. However, the accuracy of this equation depends upon how accurately you calculate the oneway time. If the actual time to send and receive the messages is not equal or approximately equal, you'll need to find some other way to calculate the one way time. However, for your purposes this should suffice.

如您所见,我们准确地将时间差计算为 2 个单位。时间差的方程式始终是sending - oneway时间。但是,此等式的准确性取决于您计算单程时间的准确程度。如果发送和接收消息的实际时间不相等或不大致相等,则您需要找到其他方法来计算单向时间。但是,对于您的目的,这应该足够了。

回答by FreePender

If you just want to sync the timeclock on several computers, NTP themselves recommend setting up your own timeserver.

如果您只是想在多台计算机上同步时钟,NTP 本身建议设置您自己的时间服务器

Server Endpoint

服务器端点

Set up an api endpoint on your server i.e.

在您的服务器上设置一个 api 端点,即

http://localhost:3000/api/time

returns:

返回:

status: 200, 
body: { time: {{currentTime}} }

How this is done will depend on the backend language that you're using.

这将如何完成取决于您使用的后端语言。

Client Code

客户代码

Given such an endpoint, this JS snippet I threw together will

鉴于这样的端点,我拼凑的这个 JS 片段将

  1. Poll your server endpoint 10 times.
  2. Attempt to compensate for latency by removing half of the round-trip time.
  3. Report the average offset between the server and the client.
  1. 轮询您的服务器端点 10 次。
  2. 尝试通过移除一半的往返时间来补偿延迟。
  3. 报告服务器和客户端之间的平均偏移量。

var offsets = [];
var counter = 0;
var maxTimes = 10;
var beforeTime = null;

// get average 
var mean = function(array) {
  var sum = 0;
  
  array.forEach(function (value) {
    sum += value;
  });
  
  return sum/array.length;
}

var getTimeDiff = function() {
  beforeTime = Date.now();
  $.ajax('/api/time', {
      type: 'GET',
      success: function(response) {
          var now, timeDiff, serverTime, offset;
          counter++;
          
          // Get offset
          now = Date.now();
          timeDiff = (now-beforeTime)/2;
          serverTime = response.data.time-timeDiff;
          offset = now-serverTime;
          
          console.log(offset);
          
          // Push to array
          offsets.push(offset)
          if (counter < maxTimes) {
            // Repeat
            getTimeDiff();
          } else {
            var averageOffset = mean(offsets);
            console.log("average offset:" + averageOffset);
          }
      }
  });
}

// populate 'offsets' array and return average offsets
getTimeDiff();

You can use this computed offset (just add it to local time), to determine a common "universal" time from each client's context.

您可以使用此计算出的偏移量(只需将其添加到本地时间),从每个客户端的上下文中确定一个通用的“通用”时间。