1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
|
//>>built
// wrapped by build app
define("dojox/cometd/timesync", ["dijit","dojo","dojox","dojo/require!dojox/cometd/_base"], function(dijit,dojo,dojox){
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: <code>{ext:{timesync:{tc:12345567890,l:23,o:4567},...},...}</code>
* where:<ul>
* <li>tc is the client timestamp in ms since 1970 of when the message was sent.
* <li>l is the network lag that the client has calculated.
* <li>o is the clock offset that the client has calculated.
* </ul>
* 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.
* <p>
* 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: <code>{ext:{timesync:{tc:12345567890,ts:1234567900,p:123,a:3},...},...}</code>
* where:<ul>
* <li>tc is the client timestamp of when the message was sent,
* <li>ts is the server timestamp of when the message was received
* <li>p is the poll duration in ms - ie the time the server took before sending the response.
* <li>a is the measured accuracy of the calculated offset and lag sent by the client
* </ul>
*
* 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: <ul>
* <li>lag = (now-tc-p)/2-a
* <li>offset = ts-tc-lag
* </ul>
*
* 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"));
});
|