13

クライアント間の時間を正確に同期する方法を探しています (少なくとも 0.5 秒としましょう)。

精度が低い (1 秒以下) ため、サーバー応答ヘッダーで jsontime を使用したり、タイムスタンプを利用したりすることは除外します。

更新: モバイル接続でも動作するはずです。3G 接続自体の往復時間が約 0.5 秒であることは珍しくありません (ここイタリアなど)。そのため、アルゴリズムは堅牢でなければなりません。

4

2 に答える 2

19

古き良き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
  1. ご覧のとおり、クライアントとサーバーのクロックは 2 単位ずれています。したがって、クライアントがタイムスタンプ リクエストを送信すると、元の時刻が 0 として記録されます。
  2. サーバーは 3 単位後にリクエストを受信しますが、2 単位先であるため、受信時刻は 5 単位として記録されます。
  3. 次に、タイムスタンプ応答を 1 単位後に送信し、送信時間を 6 単位として記録します。
  4. クライアントは 3 単位後 (つまり、サーバーによれば 9 単位) に応答を受信します。ただし、サーバーから 2 ユニット遅れているため、返された時間は 7 ユニットとして記録されます。

このデータを使用して、次のように計算できます。

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時間です。ただし、この方程式の精度は、片道時間をどれだけ正確に計算するかに依存します。メッセージを送受信する実際の時間が等しくないか、ほぼ等しくない場合は、片道の時間を計算する別の方法を見つける必要があります。ただし、目的にはこれで十分です。

于 2011-12-12T17:29:21.437 に答える
7

複数のコンピューターでタイムクロックを同期したいだけの場合は、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();

この計算されたオフセット (現地時間に追加するだけ) を使用して、各クライアントのコンテキストから共通の「ユニバーサル」時間を決定できます。

于 2015-06-12T16:19:18.987 に答える