Подтвердить что ты не робот

Синхронизировать время в javascript с хорошей точностью (> 0,5 с) (NTP-like)

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

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

UPDATE: Он должен работать даже с мобильными соединениями. Это не очень часто (например, здесь, в Италии), что 3G-соединения сами имеют время в оба конца около 0,5 с, поэтому алгоритм должен быть надежным.

4b9b3361

Ответ 1

Прибегать к старой схеме 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
  • Как вы можете видеть, часы клиента и сервера синхронизируются на 2 единицы. Следовательно, когда клиент отправляет запрос метки времени, он записывает исходное время как 0.
  • Сервер получает запрос на 3 единицы позже, но записывает время приема как 5 единиц, потому что он на 2 единицы впереди.
  • Затем он передает ответ метки времени на один блок позже и записывает время передачи как 6 единиц.
  • Клиент получает ответ после 3 единиц (т.е. на 9 единиц по серверу). Однако, поскольку он занимает 2 единицы позади сервера, он регистрирует возвращаемое время как 7 единиц.

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

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

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

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

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

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

Ответ 2

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

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

Настройте конечную точку api на своем сервере то есть.

http://localhost:3000/api/time

возвращает:

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

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

Код клиента

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

  • Опросите конечную точку сервера 10 раз.
  • Попытка компенсировать задержку, удалив половину времени прохождения.
  • Сообщите среднее смещение между сервером и клиентом.

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