2011-12-12 56 views
12

Tôi đang tìm cách đồng bộ hóa thời gian giữa các khách hàng với độ chính xác cao (ít nhất là 0,5 giây).Đồng bộ hóa thời gian trong javascript với độ chính xác cao (> 0.5s) (giống như NTP)

Tôi loại trừ sử dụng jsontime hoặc khai thác dấu thời gian trong tiêu đề phản hồi máy chủ do độ chính xác kém (giây hoặc ít hơn).

CẬP NHẬT: Nó sẽ hoạt động ngay cả với kết nối di động. Nó không phải là không thường xuyên (ví dụ ở đây ở Ý) rằng các kết nối 3G chính nó có một chuyến đi vòng quanh thời gian khoảng 0,5 giây, do đó, thuật toán phải mạnh mẽ.

Trả lời

16

Khu nghỉ dưỡng theo sơ đồ thông báo cũ ICMP Timestamp. Nó khá tầm thường để thực hiện trong JavaScript và PHP.

Dưới đây là một thực hiện chương trình này sử dụng JavaScript và 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 
    } 
} 

Bây giờ cho mã sync.php:

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

tôi đã không kiểm tra mã trên, nhưng nó phải làm việc.

Lưu ý: Phương pháp sau đây sẽ tính toán chính xác chênh lệch thời gian giữa máy khách và máy chủ với thời gian gửi và nhận thực tế giống nhau hoặc gần như nhau. Hãy xem xét các tình huống sau:

Time Client Server 
-------- -------- -------- 
Original  0  2 
Receive   3  5 
Transmit  4  6 
Returned  7  9 
  1. Như bạn có thể thấy, client và server đồng hồ là 2 đơn vị tắt đồng bộ hóa. Do đó khi khách hàng gửi yêu cầu dấu thời gian, nó ghi lại thời gian ban đầu là 0.
  2. Máy chủ nhận được yêu cầu 3 đơn vị sau đó, nhưng ghi lại thời gian nhận là 5 đơn vị vì nó là 2 đơn vị phía trước.
  3. Sau đó, nó truyền dấu thời gian trả lời một đơn vị sau đó và ghi lại thời gian truyền là 6 đơn vị.
  4. Khách hàng nhận được trả lời sau 3 đơn vị (tức là tại 9 đơn vị theo máy chủ). Tuy nhiên, vì nó là 2 đơn vị phía sau máy chủ, nó ghi lại thời gian trả về là 7 đơn vị.

Sử dụng dữ liệu này, chúng tôi có thể tính toán:

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

Như bạn có thể nhìn thấy từ trên, gửi và nhận lần được tính toán sai tùy thuộc vào bao nhiêu client và server là tắt đồng bộ hóa. Tuy nhiên, thời gian khứ hồi sẽ luôn đúng bởi vì chúng ta lần đầu tiên thêm hai đơn vị (nhận + bản gốc), và sau đó trừ hai đơn vị (trả về - truyền).

Nếu chúng ta giả định rằng thời gian oneway luôn là một nửa thời gian khứ hồi (tức là thời gian để truyền tải là thời gian để tiếp nhận, sau đó chúng ta có thể dễ dàng tính toán sự khác biệt thời gian như sau):

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

Như bạn có thể thấy, chúng tôi tính toán chính xác chênh lệch thời gian là 2 đơn vị. Phương trình cho chênh lệch thời gian luôn là sending - oneway. Tuy nhiên, độ chính xác của phương trình này phụ thuộc vào độ chính xác mà bạn tính toán theo thời gian. Nếu thời gian thực gửi và nhận tin nhắn không bằng hoặc xấp xỉ bằng nhau, bạn sẽ cần phải tìm cách khác để tính thời gian một chiều.Tuy nhiên, cho mục đích của bạn, điều này là đủ.

+0

Tôi làm cách nào để gửi các gói ICMP từ javascript? Tôi đã nhìn thấy chỉ có một ActiveX được gọi là ActiveSocket để gửi các gói ICMP nhưng tôi không thể sử dụng ActiveX.It shoud làm việc trên webkit. – micred

+0

Bạn không gửi gói ICMP. Bạn chỉ cần sử dụng cùng một phương thức mà ICMP sử dụng để đồng bộ hóa thời gian trên máy chủ và máy khách. Hy vọng rằng rõ ràng nghi ngờ của bạn. Chúc mừng. ;) –

+1

Định dạng của bạn trong PHP có vẻ không đúng: 'microtime()' không có tham số trả về một chuỗi có dạng "0.uuuuuu ssssssssss". Tôi muốn 'echo vòng (microtime (true) * 1000)'. –

3

Nếu bạn chỉ muốn đồng bộ hóa mã thời gian trên một số máy tính, NTP tự giới thiệu setting up your own timeserver.

server Endpoint

Thiết lập một endpoint api trên máy chủ của bạn tức

http://localhost:3000/api/time 

lợi nhuận:

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

thế nào điều này được thực hiện sẽ phụ thuộc vào ngôn ngữ phụ trợ mà bạn sử dụng.

Khách hàng Mã

Với như một thiết bị đầu cuối, điều này JS đoạn tôi đã ném lại với nhau sẽ

  1. Poll endpoint máy chủ của bạn 10 lần.
  2. Cố gắng bù đắp thời gian chờ bằng cách xóa một nửa thời gian khứ hồi.
  3. Báo cáo mức chênh lệch trung bình giữa máy chủ và ứng dụng khách.

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

Bạn có thể sử dụng này tính toán bù đắp (chỉ cần thêm nó vào giờ địa phương), để xác định một chung "phổ quát" thời gian từ bối cảnh của từng khách hàng.

+1

Điều này hữu ích, cảm ơn. Nó cũng có thể có ý nghĩa để sử dụng trung bình thay vì có nghĩa là để từ chối ngoại lệ. – Groo