2011-12-12 8 views
12

के साथ जावास्क्रिप्ट में समय सिंक्रनाइज़ करें मैं ग्राहकों के बीच एक अच्छा परिशुद्धता के साथ समय सिंक्रनाइज़ करने का एक तरीका ढूंढ रहा हूं (चलो कम से कम 0.5 सेकंड कहें)।एक अच्छी परिशुद्धता (> 0.5s) (एनटीपी-जैसे)

मैं खराब परिशुद्धता (एक सेकंड या शायद कम) के कारण सर्वर प्रतिक्रिया शीर्षकों में टाइमस्टैम्प का उपयोग करने या टाइमस्टैम्प का उपयोग करने से बाहर निकलता हूं।

अद्यतन: इसे मोबाइल कनेक्शन के साथ भी काम करना चाहिए। यह अनिश्चित नहीं है (उदाहरण के लिए इटली में) कि 3 जी कनेक्शन के पास एक गोल यात्रा समय लगभग 0.5 है, इसलिए एल्गोरिदम मजबूत होना चाहिए।

उत्तर

16

अच्छी पुरानी ICMP Timestamp संदेश योजना का रिज़ॉर्ट। यह जावास्क्रिप्ट और 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 का उपयोग का एक कार्यान्वयन है

<?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 इकाइयों को प्राप्त करता है, लेकिन प्राप्त समय को 5 इकाइयों के रूप में रिकॉर्ड करता है क्योंकि यह 2 इकाइयां आगे है।
  3. फिर यह टाइमस्टैम्प उत्तर को एक इकाई बाद में प्रसारित करता है और प्रेषण समय को 6 इकाइयों के रूप में रिकॉर्ड करता है।
  4. ग्राहक को 3 इकाइयों के बाद जवाब प्राप्त होता है (यानी सर्वर के अनुसार 9 इकाइयों पर)। हालांकि, चूंकि यह सर्वर के पीछे 2 इकाइयां है, इसलिए यह लौटा हुआ समय 7 इकाइयों के रूप में रिकॉर्ड करता है।

इस डेटा का उपयोग करना, हम गणना कर सकते हैं:

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

आप ऊपर से देख सकते हैं, भेजने और प्राप्त करने के समय को गलत तरीके से पर कितना क्लाइंट और सर्वर सिंक बंद कर रहे हैं निर्भर करता है गणना कर रहे हैं। हालांकि, राउंडट्रिप समय हमेशा सही होगा क्योंकि हम पहले दो इकाइयां जोड़ रहे हैं (मूल प्राप्त करें), और फिर दो इकाइयों को घटाकर (लौटाया गया - प्रेषण)।

अगर हम मान लेते हैं कि oneway समय हमेशा गोल यात्रा आधे समय के लिए है (यानी समय संचारित करने के लिए समय प्राप्त करने के लिए है, तो हम आसानी से समय अंतर के रूप में इस गणना कर सकते हैं):

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

के रूप में आप देख सकते हैं, हमने सटीक रूप से 2 इकाइयों के रूप में समय अंतर की गणना की। समय अंतर के लिए समीकरण हमेशा sending - oneway समय होता है। हालांकि, इस समीकरण की सटीकता इस बात पर निर्भर करती है कि आप कितनी सटीक समय पर गणना करते हैं। यदि संदेश भेजने और प्राप्त करने का वास्तविक समय बराबर या लगभग बराबर नहीं है, तो आपको एक ही समय की गणना करने के लिए कुछ और तरीका ढूंढना होगा।हालांकि, आपके उद्देश्यों के लिए यह पर्याप्त होना चाहिए।

+0

मैं जावास्क्रिप्ट से आईसीएमपी पैकेट कैसे भेज सकता हूं? मैंने आईसीएमपी पैकेट भेजने के लिए केवल ActiveXocket नामक एक ActiveX देखा है, लेकिन मैं ActiveX का उपयोग नहीं कर सकता। यह वेबकिट पर जोरदार काम करता है। – micred

+0

आप आईसीएमपी पैकेट नहीं भेजते हैं। आप बस उसी विधि का उपयोग करते हैं जो आईसीएमपी सर्वर और क्लाइंट पर समय सिंक्रनाइज़ करने के लिए उपयोग करता है। उम्मीद है कि आपके संदेह को साफ़ करता है। चीयर्स। ;) –

+1

PHP में आपका स्वरूपण गलत दिखता है: 'पैरामीटर के साथ' microtime() 'फॉर्म "0.uuuuuu ssssssssss" की स्ट्रिंग देता है। मैं बल्कि गूंज (microtime (सच) * 1000) 'गूंजना होगा। –

3

यदि आप कई कंप्यूटरों पर टाइमक्लॉक सिंक करना चाहते हैं, तो एनटीपी स्वयं setting up your own timeserver की अनुशंसा करता है।

सर्वर Endpoint

यानी

http://localhost:3000/api/time 

रिटर्न अपने सर्वर पर एक API समाप्ति बिंदु सेट करें:

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

यह कैसे बैकएंड भाषा आप कर रहे हैं कि इस पर निर्भर करेगा किया जाता है का उपयोग करते हुए।

क्लाइंट कोड

इस तरह के एक अंत बिंदु को देखते हुए इसे टुकड़ा मैं एक साथ होगा

  1. पोल अपने सर्वर endpoint 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();

आप कर सकते हैं का उपयोग इस गणना की भरपाई (सिर्फ स्थानीय समय के लिए इसे जोड़ने), प्रत्येक ग्राहक के संदर्भ से एक आम "सार्वभौमिक" समय निर्धारित करने के लिए।

+1

यह उपयोगी है, धन्यवाद। आउटलियर को अस्वीकार करने के लिए औसत के बजाय औसत का उपयोग करना भी समझ सकता है। – Groo

संबंधित मुद्दे