org.cometd.server.ext.TimesyncExtension Maven / Gradle / Ivy
/*
* Copyright (c) 2008-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.cometd.server.ext;
import java.util.HashMap;
import java.util.Map;
import org.cometd.bayeux.server.BayeuxServer.Extension;
import org.cometd.bayeux.server.ServerMessage;
import org.cometd.bayeux.server.ServerMessage.Mutable;
import org.cometd.bayeux.server.ServerSession;
/**
* Timesync extension (server side).
*
* With each handshake or connect, the extension sends timestamps within the ext
* field like:
* {ext:{timesync:{tc:12345567890,l:23,o:4567},...},...}
where:
*
* - tc is the client timestamp in ms since 1970 of when the message was sent.
*
- l is the network lag that the client has calculated.
*
- o is the clock offset that the client has calculated.
*
*
*
* A cometd server that supports timesync, can respond with an ext field like:
* {ext:{timesync:{tc:12345567890,ts:1234567900,p:123,a:3},...},...}
* where:
*
* - tc is the client timestamp of when the message was sent,
*
- ts is the server timestamp of when the message was received
*
- p is the poll duration in ms - ie the time the server took before sending
* the response.
*
- a is the measured accuracy of the calculated offset and lag sent by the
* client
*
*
* The relationship between tc, ts, o and l on the server is given by
* ts=tc+o+l
(the time the server received the message is the
* client time plus the offset plus the network lag). Thus the accuracy of the o
* and l settings can be determined with a=tc+o+l-ts
.
*
*
* When the client has received the response, it can make a more accurate
* estimate of the lag as l2=(now-tc-p)/2
(assuming symmetric lag).
* A new offset can then be calculated with the relationship on the client that
* ts=tc+o2+l2
, thus o2=ts-tc-l2
.
*
*
* Since the client also receives the a value calculated on the server, it
* should be possible to analyse this and compensate for some asymmetry in the
* lag. But the current client does not do this.
*
*/
public class TimesyncExtension extends Extension.Adapter {
public static final String LAG_ATTRIBUTE = "lag";
private int _accuracyTarget = 25;
public TimesyncExtension() {
}
/**
* timesync responses are not set if the measured accuracy is less than the
* accuracyTarget.
*
* @return accuracy target in ms (default 25ms)
*/
public int getAccuracyTarget() {
return _accuracyTarget;
}
/**
* timesync responses are not set if the measured accuracy is less than the
* accuracyTarget.
*
* @param target accuracy target in ms
*/
public void setAccuracyTarget(int target) {
_accuracyTarget = target;
}
@Override
public boolean rcvMeta(ServerSession from, Mutable message) {
Map ext = message.getExt(false);
if (ext != null) {
Map sync = (Map)ext.get("timesync");
if (sync != null) {
sync.put("ts", System.currentTimeMillis());
Number lag = (Number)sync.get("l");
if (lag != null && from != null) {
from.setAttribute(LAG_ATTRIBUTE, lag);
}
}
}
return true;
}
@Override
public boolean sendMeta(ServerSession session, Mutable message) {
ServerMessage associated = message.getAssociated();
if (associated != null) {
Map extIn = associated.getExt();
if (extIn != null) {
Map sync = (Map)extIn.get("timesync");
if (sync != null) {
final long tc = ((Number)sync.get("tc")).longValue();
final long ts = ((Number)sync.get("ts")).longValue();
final Number lag = (Number)sync.get("l");
if (lag == null) {
// old style timesync
Map extOut = message.getExt(true);
Map timesync = new HashMap<>(3);
timesync.put("tc", tc);
timesync.put("ts", ts);
timesync.put("p", System.currentTimeMillis() - ts);
extOut.put("timesync", timesync);
} else {
final int l = lag.intValue();
final int o = ((Number)sync.get("o")).intValue();
final int a = (int)((tc + o + l) - ts);
// is a OK ?
if (l == 0 || a >= _accuracyTarget || a <= -_accuracyTarget) {
Map extOut = message.getExt(true);
Map timesync = new HashMap<>(4);
timesync.put("tc", tc);
timesync.put("ts", ts);
timesync.put("p", System.currentTimeMillis() - ts);
timesync.put("a", a);
extOut.put("timesync", timesync);
}
}
}
}
}
return true;
}
}