dojo.provide("dojox.cometd.timesync"); dojo.require("dojox.cometd._base"); /** * this file provides the time synchronization extension to cometd. * Timesync allows the client and server to exchange time information on every * handshake and connect message so that the client may calculate an approximate * offset from it's own clock epoch to that of the server. * * With each handshake or connect, the extension sends timestamps within the * ext field like: {ext:{timesync:{tc:12345567890,l:23,o:4567},...},...} * where: * The accuracy of the offset and lag may be calculated with tc-now-l-o, * which should be zero if the calculated offset and lag are perfectly * accurate. *

* A cometd server that supports timesync, should respond only if the * measured accuracy value is greater than accuracy target. The response * will be an ext field like: {ext:{timesync:{tc:12345567890,ts:1234567900,p:123,a:3},...},...} * where:

* * On receipt of the response, the client is able to use current time to determine * the total trip time, from which p is subtracted to determine an approximate * two way network traversal time. The measured accuracy is used to adjust the assumption * that the network is symmetric for traversal time, so: * * In order to smooth over any transient fluctuations, the extension keeps a sliding * average of the offsets received. By default this is over 10 messages, but this can * be changed with the dojox.cometd.timesync._window element. */ dojox.cometd.timesync = new function(){ this._window = 10; // The window size for the sliding average of offset samples. this._lags = []; // The samples used to calculate the average lag. this._offsets = []; // The samples used to calculate the average offset. this.lag=0; // The calculated network lag from client to server this.offset = 0; // The offset in ms between the clients clock and the servers clock. this.samples = 0; // The number of samples used to calculate the offset. If 0, the offset is not valid. this.getServerTime = function(){ // return: long // Summary: // Calculate the current time on the server // return new Date().getTime()+this.offset; } this.getServerDate = function(){ // return: Date // Summary: // Calculate the current time on the server // return new Date(this.getServerTime()); } this.setTimeout = function(/*function*/call, /*long|Date*/atTimeOrDate){ // Summary: // Set a timeout function relative to server time // call: // the function to call when the timeout occurs // atTimeOrTime: // a long timestamp or a Date representing the server time at // which the timeout should occur. var ts = (atTimeOrDate instanceof Date) ? atTimeOrDate.getTime() : (0 + atTimeOrDate); var tc = ts - this.offset; var interval = tc - new Date().getTime(); if(interval <= 0){ interval = 1; } return setTimeout(call,interval); } this._in = function(/*Object*/msg){ // Summary: // Handle incoming messages for the timesync extension. // description: // Look for ext:{timesync:{}} field and calculate offset if present. // msg: // The incoming bayeux message var channel = msg.channel; if(channel && channel.indexOf('/meta/') == 0){ if(msg.ext && msg.ext.timesync){ var sync = msg.ext.timesync; var now = new Date().getTime(); var l=(now-sync.tc-sync.p)/2-sync.a; var o=sync.ts-sync.tc-l; this._lags.push(l); this._offsets.push(o); if(this._offsets.length > this._window){ this._offsets.shift(); this._lags.shift(); } this.samples++; l=0; o=0; for(var i in this._offsets){ l+=this._lags[i]; o+=this._offsets[i]; } this.offset = parseInt((o / this._offsets.length).toFixed()); this.lag = parseInt((l / this._lags.length).toFixed()); } } return msg; } this._out = function(msg){ // Summary: // Handle outgoing messages for the timesync extension. // description: // Look for handshake and connect messages and add the ext:{timesync:{}} fields // msg: // The outgoing bayeux message var channel = msg.channel; if(channel && channel.indexOf('/meta/') == 0){ var now = new Date().getTime(); if(!msg.ext){ msg.ext = {}; } msg.ext.timesync = {tc:now,l:this.lag,o:this.offset}; } return msg; } }; dojox.cometd._extendInList.push(dojo.hitch(dojox.cometd.timesync, "_in")); dojox.cometd._extendOutList.push(dojo.hitch(dojox.cometd.timesync, "_out"));