クライアント間の時間を正確に同期する方法を探しています (少なくとも 0.5 秒としましょう)。
精度が低い (1 秒以下) ため、サーバー応答ヘッダーで jsontime を使用したり、タイムスタンプを利用したりすることは除外します。
更新: モバイル接続でも動作するはずです。3G 接続自体の往復時間が約 0.5 秒であることは珍しくありません (ここイタリアなど)。そのため、アルゴリズムは堅牢でなければなりません。
クライアント間の時間を正確に同期する方法を探しています (少なくとも 0.5 秒としましょう)。
精度が低い (1 秒以下) ため、サーバー応答ヘッダーで jsontime を使用したり、タイムスタンプを利用したりすることは除外します。
更新: モバイル接続でも動作するはずです。3G 接続自体の往復時間が約 0.5 秒であることは珍しくありません (ここイタリアなど)。そのため、アルゴリズムは堅牢でなければなりません。
古き良きICMP タイムスタンプメッセージ スキームに頼ります。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
このデータを使用して、次のように計算できます。
Sending = Receive - Original = 5 - 0 = 5
Receiving = Returned - Transmit = 7 - 6 = 1
Roundtrip = Sending + Receiving = 5 + 1 = 6
上記からわかるように、クライアントとサーバーがどの程度同期していないかによって、送信時間と受信時間が正しく計算されません。ただし、最初に 2 つのユニット (受信 + オリジナル) を加算し、次に 2 つのユニット (返送 - 送信) を減算するため、往復時間は常に正確です。
片道の時間が常に往復時間の半分であると仮定すると (つまり、送信する時間が受信する時間である場合、時間差は次のように簡単に計算できます)。
Oneway = Roundtrip / 2 = 6 / 2 = 3
Difference = Sending - Oneway = 5 - 3 = 2
ご覧のとおり、時差を 2 単位として正確に計算しました。時差の方程式は常にsending - oneway
時間です。ただし、この方程式の精度は、片道時間をどれだけ正確に計算するかに依存します。メッセージを送受信する実際の時間が等しくないか、ほぼ等しくない場合は、片道の時間を計算する別の方法を見つける必要があります。ただし、目的にはこれで十分です。
複数のコンピューターでタイムクロックを同期したいだけの場合は、NTP 自身が独自のタイムサーバーをセットアップすることを推奨しています。
サーバーにAPIエンドポイントを設定します。つまり
http://localhost:3000/api/time
戻り値:
status: 200,
body: { time: {{currentTime}} }
これがどのように行われるかは、使用しているバックエンド言語によって異なります。
このようなエンドポイントが与えられた場合、私が一緒に投げたこの JS スニペットは
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();
この計算されたオフセット (現地時間に追加するだけ) を使用して、各クライアントのコンテキストから共通の「ユニバーサル」時間を決定できます。