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

Лучший способ синхронизации клиентских javascript-часов с датой сервера

У меня есть задача показать цифровые часы (с точностью до минут) на странице HTML в некотором фиксированном часовом поясе (MSK или MSD - в зависимости от текущей даты). Я бы хотел не полагаться на часы клиентской системы, поэтому требуется некоторая синхронизация с сервером. HTTP-сервер отправляет заголовок даты в каждом ответе, поэтому мы можем отправить запрос AJAX GET или HEAD на любой URL нашего сайта, чтобы получить дату сервера, рассчитать разницу с датой клиента и использовать его при обновлении часов с помощью setTimeout(). Остаются другие проблемы: переключение часового пояса для настроек дневного света, учет задержки для очень медленных соединений.

Любая идея для этой задачи проще всего? Я бы предпочел решить его без серверного программирования.

4b9b3361

Ответ 1

вы должны помнить время клиента между readyState == 2 и readyState == 3, если вы собираетесь использовать ajax, потому что время сервера будет установлено где-то между временем по запросу и полученным ответом

Ответ 2

Эти две функции 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() берет время, чтобы выполнить, вы можете потерять некоторую секундную точность. Возможно, время исполнения может быть принято во внимание....

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

Пример работает только в IE из-за "Msxml2.XMLHTTP", я думаю.....

Ответ 3

Вы можете рассчитать точное время с помощью NTP (Network Time Protocol) в ваших кодах,

Я попытаюсь объяснить вам:

  • У нас есть ClientTime при отправке запроса (например, 4/3/2012 13: 56: 10.123)
  • Вы отправляете ClientTime на сервер
  • У нас есть Время в обратном направлении для запроса, я назвал его RequestTime (например: он занимает 5 секунд).
  • В сервере мы вычисляем время разницы между сервером и клиентом (например: It ServerTime - ClientTime = ServerClientDifferenceTimeWithRequestTime), вы должны теперь эту Разницу, включая время запроса на круговое путешествие на шаге 3, тогда вы должны удалить время прохождения в оба конца из Различия
  • Отправить ответ сервера, который включает ServerClientDifferenceTimeWithRequestTime и ServerTime
  • У нас есть Время в пути, я назвал его ResponseTime (например: он занимает 3 секунды)
  • В клиенте мы снова вычисляем время разницы между сервером и клиентом (например: It ServerTime - ClientTime = ServerClientDifferenceTimeWithResponseTime), опять же: теперь вы должны это различие, включая время отклика в обратном направлении на шаге 6
  • У нас есть время в клиенте
  • Вы должны рассчитать простые уравнения в клиенте:

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

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

Now - ClientTime = RquestTime + ResponseTime = >

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

если вы решите его, вы нашли это:

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

а затем вы можете найти синхронизированное время или время сервера в клиенте с этим уравнением:

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

Я показываю простой код, но когда вы хотите написать его, не забывайте использовать функции даты и времени UTC...

Сторона сервера (например, 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}";

С#

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

Клиентская сторона (Javascript с Jquery):

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

Ответ 4

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

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

сторона браузера (javascript):

// the NTP algorithm
// t0 is the client timestamp of the request packet transmission,
// t1 is the server timestamp of the request packet reception,
// t2 is the server timestamp of the response packet transmission and
// t3 is the client 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 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 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);

Примечание

Этот способ можно рассматривать как своеобразное гетто, есть и другие ответы, связанные с переполнением стека, которые помогут вам найти лучшее решение:

Ответ 5

Я бы запросил обновление только с сервера каждые 30 или около того, если вам нужна точность только до минуты. Не полагайтесь на время клиента вообще, но используйте их системные часы, чтобы поддерживать точность часов между обновлениями. Думаю, вы ответили на свой вопрос?

Это поможет, если мы лучше поймем, что вы на самом деле пытаетесь сделать.

Если вы просто хотите, чтобы часы отображали время на сервере, которое затем настраивается на определенный часовой пояс, делайте это с клиентами с помощью смещений. Управляйте DST в часовых поясах, это применимо, используя дату, которую вы получаете с сервера. Если вы хотите определить задержку, вам, вероятно, понадобится небольшой script на сервере для расчета разницы. Но, как и выше, это поможет лучше понять проблему. Если точность только до минуты, латентность кажется менее критичной.

Ответ 7

Я бы синхронизировал время во время инициализации с сервером интернет-времени.

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