Synchronize time in javascript with a good precision (>0.5s) (NTP-like)

前端 未结 2 1388
谎友^
谎友^ 2020-12-13 15:58

I\'m looking for a way to synchronize time between clients with a good precision (let\'s say 0.5 seconds at least).

I exclude using jsontime or exploiting timestamp

相关标签:
2条回答
  • 2020-12-13 16:36

    If you just want to sync the timeclock on several computers, NTP themselves recommend setting up your own timeserver.

    Server Endpoint

    Set up an api endpoint on your server i.e.

    http://localhost:3000/api/time
    

    returns:

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

    How this is done will depend on the backend language that you're using.

    Client Code

    Given such an endpoint, this JS snippet I threw together will

    1. Poll your server endpoint 10 times.
    2. Attempt to compensate for latency by removing half of the round-trip time.
    3. Report the average offset between the server and the client.

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

    You can use this computed offset (just add it to local time), to determine a common "universal" time from each client's context.

    0 讨论(0)
  • 2020-12-13 16:43

    Resort to the good old ICMP Timestamp message scheme. It's fairly trivial to implement in JavaScript and PHP.

    Here's an implementation of this scheme using JavaScript and 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
        }
    }
    

    Now for the sync.php code:

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

    I haven't tested the above code, but it should work.

    Note: The following method will accurately calculate the time difference between the client and the server provided that actual time to send and receive messages is the same or approximately the same. Consider the following scenario:

      Time    Client   Server
    -------- -------- --------
    Original        0        2
    Receive         3        5
    Transmit        4        6
    Returned        7        9
    
    1. As you can see, the client and server clocks are 2 units off sync. Hence when the client sends the timestamp request, it records the original time as 0.
    2. The server receives the request 3 units later, but records the receive time as 5 units because it's 2 units ahead.
    3. Then it transmits the timestamp reply one unit later and records the transmit time as 6 units.
    4. The client receives the reply after 3 units (i.e. at 9 units according to the server). However, since it's 2 units behind the server it records the returned time as 7 units.

    Using this data, we can calculate:

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

    As you can see from above, the sending and receiving times are calculated incorrectly depending upon how much the client and server are off sync. However, the roundtrip time will always be correct because we are first adding two units (receive + original), and then subtracting two units (returned - transmit).

    If we assume that the oneway time is always half of the roundtrip time (i.e. the time to transmit is the time to receive, then we can easily calculate the time difference as follows):

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

    As you can see, we accurately calculated the time difference as 2 units. The equation for time difference is always sending - oneway time. However, the accuracy of this equation depends upon how accurately you calculate the oneway time. If the actual time to send and receive the messages is not equal or approximately equal, you'll need to find some other way to calculate the one way time. However, for your purposes this should suffice.

    0 讨论(0)
提交回复
热议问题