Синхронизируйте время в JavaScript с хорошей точностью (> 0,5 с) (как NTP) - PullRequest
12 голосов
/ 12 декабря 2011

Я ищу способ синхронизации времени между клиентами с хорошей точностью (скажем, не менее 0,5 секунды).

Я исключаю использование jsontime или использование метки времени в заголовках ответа сервера из-за низкой точности(секунда или, может быть, меньше).

ОБНОВЛЕНИЕ: должно работать даже с мобильными соединениями.Нередко (например, здесь, в Италии) само соединение 3G имеет время прохождения сигнала туда и обратно около 0,5 с, поэтому алгоритм должен быть надежным.

Ответы [ 2 ]

16 голосов
/ 12 декабря 2011

Обращайтесь к старой доброй ICMP Timestamp схеме сообщений.Это довольно тривиально для реализации на JavaScript и 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
    }
}

Теперь для кода sync.php:

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

Я не проверял приведенный выше код, но он должен работать.

Примечание: Следующий метод позволяет точно рассчитать разницу во времени между клиентом и сервером при условии, что фактическое времяотправлять и получать сообщения одинаково или примерно одинаково.Рассмотрим следующий сценарий:

  Time    Client   Server
-------- -------- --------
Original        0        2
Receive         3        5
Transmit        4        6
Returned        7        9
  1. Как видите, часы клиента и сервера на 2 единицы не синхронизированы.Следовательно, когда клиент отправляет запрос метки времени, он записывает исходное время как 0.
  2. Сервер получает запрос спустя 3 единицы, но записывает время приема как 5 единиц, потому что это на 2 единицы впереди.
  3. Затем он передает ответ с меткой времени на одну единицу позже и записывает время передачи как 6 единиц.
  4. Клиент получает ответ через 3 единицы (т. Е. При 9 единицах в зависимости от сервера).Однако, поскольку он находится на 2 единицы позади сервера, он записывает возвращаемое время как 7 единиц.

Используя эти данные, мы можем вычислить:

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

Как видно из приведенного вышевремя отправки и получения рассчитывается неверно в зависимости от того, насколько не синхронизированы клиент и сервер.Тем не менее, время туда и обратно всегда будет правильным, потому что мы сначала добавляем две единицы (получи + оригинал), а затем вычитаем две единицы (возвращено - передай).время туда-обратно (т.е. время для передачи - это время для получения, тогда мы можем легко рассчитать разницу во времени следующим образом):

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

Как вы можете видеть, мы точно рассчитали разницу во времени как 2 единицы,Уравнение для разницы во времени всегда sending - oneway время.Однако точность этого уравнения зависит от того, насколько точно вы рассчитываете время в пути.Если фактическое время отправки и получения сообщений не равно или приблизительно равно, вам нужно будет найти какой-то другой способ вычисления времени в один конец.Однако для ваших целей этого должно быть достаточно.

3 голосов
/ 12 июня 2015

Если вы просто хотите синхронизировать временные часы на нескольких компьютерах, сами NTP рекомендуют настроить свой собственный сервер времени .

Конечная точка сервера

Настройка конечной точки API на вашем сервере т.е.

http://localhost:3000/api/time

возвращает:

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

Как это сделать, будет зависеть от используемого вами языка бэкэнда.

Код клиента

Учитывая такую ​​конечную точку, этот фрагмент JS, который я создал вместе, будет

  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();

Вы можете использовать это вычисленное смещение (просто добавьте его к местному времени), чтобы определить общее "универсальное" время из контекста каждого клиента.

...