From 732c0aab41eec4601f7b33ae7d2018808c829412 Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Fri, 8 Aug 2003 20:20:39 +0000 Subject: [PATCH] Modified clock synchronization approach slightly so that we allow 5 seconds to pass in between ping/pong latency samples. Additionally, we resync the clock every 10 minutes. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@2748 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- .../threerings/presents/client/Client.java | 104 ++++++++++++------ .../presents/client/Communicator.java | 48 ++------ 2 files changed, 81 insertions(+), 71 deletions(-) diff --git a/src/java/com/threerings/presents/client/Client.java b/src/java/com/threerings/presents/client/Client.java index 4c3580024..a2e33cc65 100644 --- a/src/java/com/threerings/presents/client/Client.java +++ b/src/java/com/threerings/presents/client/Client.java @@ -1,5 +1,5 @@ // -// $Id: Client.java,v 1.39 2003/07/17 21:39:15 mdb Exp $ +// $Id: Client.java,v 1.40 2003/08/08 20:20:39 mdb Exp $ package com.threerings.presents.client; @@ -8,6 +8,8 @@ import java.util.List; import com.samskivert.util.ObserverList; import com.samskivert.util.ResultListener; +import com.samskivert.util.Interval; +import com.samskivert.util.IntervalManager; import com.threerings.presents.Log; import com.threerings.presents.data.ClientObject; @@ -322,6 +324,18 @@ public class Client // this will initiate the logon process _comm = new Communicator(this); _comm.logon(); + + // register an interval that we'll use to keep the clock synced + // and to send pings when appropriate + _piid = IntervalManager.register(new Interval() { + public void intervalExpired (int id, Object arg) { + if (id != _piid) { + IntervalManager.remove(id); + } else { + tick(); + } + } + }, 5000L, null, true); } /** @@ -351,6 +365,9 @@ public class Client return false; } + // kill our tick interval + _piid = -1; + // ask the communicator to send a logoff message and disconnect // from the server _comm.logoff(); @@ -358,34 +375,46 @@ public class Client return true; } + /** + * Called every five seconds; ensures that we ping the server if we + * haven't communicated in a long while and periodically resyncs the + * client and server clock deltas. + */ + protected void tick () + { + long now = System.currentTimeMillis(); + + if (_dcalc != null) { + // if we're syncing the clock, send another ping + PingRequest req = new PingRequest(); + _comm.postMessage(req); + _dcalc.sentPing(req); + + } else if (now - _comm.getLastWrite() > PingRequest.PING_INTERVAL) { + // if we haven't sent anything over the network in a while, we + // ping the server to let it know that we're still alive + _comm.postMessage(new PingRequest()); + + } else if (now - _lastSync > CLOCK_SYNC_INTERVAL) { + // resync our clock with the server + establishClockDelta(now); + } + } + /** * Called during initialization to initiate a sequence of ping/pong * messages which will be used to determine (with "good enough" * accuracy) the difference between the client clock and the server * clock so that we can later interpret server timestamps. */ - protected void establishClockDelta () + protected void establishClockDelta (long now) { // create a new delta calculator and start the process _dcalc = new DeltaCalculator(); PingRequest req = new PingRequest(); _comm.postMessage(req); _dcalc.sentPing(req); - } - - /** - * This is called when we've completed the process of pinging the - * server a few times to establish our clock delta. - */ - protected void clockDeltaEstablished () - { - // initialize our invocation director - _invdir.init(_comm.getDObjectManager(), _cloid, this); - - // we can't quite call initialization completed at this point - // because we need for the invocation director to fully initialize - // (which requires a round trip to the server) before turning the - // client loose to do things like request invocation services + _lastSync = now; } /** @@ -484,9 +513,17 @@ public class Client // extract bootstrap information _cloid = data.clientOid; + // initialize our invocation director + _invdir.init(_comm.getDObjectManager(), _cloid, this); + // send a few pings to the server to establish the clock offset // between this client and server standard time - establishClockDelta(); + establishClockDelta(System.currentTimeMillis()); + + // we can't quite call initialization completed at this point + // because we need for the invocation director to fully initialize + // (which requires a round trip to the server) before turning the + // client loose to do things like request invocation services } /** @@ -502,21 +539,15 @@ public class Client return; } - if (_dcalc.gotPong(pong)) { - // we're either done with our calculations, so we can grab the - // time delta and finish our business... - _serverDelta = _dcalc.getTimeDelta(); - // free up our delta calculator - _dcalc = null; - // let the client continue with its initialization - clockDeltaEstablished(); - Log.info("Time offset from server: " + _serverDelta + "ms."); + // we update the delta after every receipt so as to immediately + // obtain an estimate of the clock delta and then refine it as + // more packets come in + boolean done = _dcalc.gotPong(pong); + _serverDelta = _dcalc.getTimeDelta(); - } else { - // ...or we'll either be sending another ping - PingRequest req = new PingRequest(); - _comm.postMessage(req); - _dcalc.sentPing(req); + if (done) { + Log.info("Time offset from server: " + _serverDelta + "ms."); + _dcalc = null; } } @@ -637,6 +668,15 @@ public class Client * server. */ protected DeltaCalculator _dcalc; + /** The last time at which we synced our clock with the server. */ + protected long _lastSync; + + /** Our tick interval id. */ + protected int _piid = -1; + + /** How often we recompute our time offset from the server. */ + protected static final long CLOCK_SYNC_INTERVAL = 600 * 1000L; + // client observer codes static final int CLIENT_DID_LOGON = 0; static final int CLIENT_FAILED_TO_LOGON = 1; diff --git a/src/java/com/threerings/presents/client/Communicator.java b/src/java/com/threerings/presents/client/Communicator.java index 828c85ed4..f6ea87976 100644 --- a/src/java/com/threerings/presents/client/Communicator.java +++ b/src/java/com/threerings/presents/client/Communicator.java @@ -1,5 +1,5 @@ // -// $Id: Communicator.java,v 1.32 2003/07/17 21:39:15 mdb Exp $ +// $Id: Communicator.java,v 1.33 2003/08/08 20:20:39 mdb Exp $ package com.threerings.presents.client; @@ -13,8 +13,6 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import com.samskivert.io.NestableIOException; -import com.samskivert.util.Interval; -import com.samskivert.util.IntervalManager; import com.samskivert.util.LoopingThread; import com.samskivert.util.Queue; import com.samskivert.util.RuntimeAdjust; @@ -96,15 +94,6 @@ public class Communicator // start up the writer thread if everything went successfully _reader = new Reader(); _reader.start(); - - // register an interval to send pings when appropriate - _piid = IntervalManager.register(new Interval() { - public void intervalExpired (int id, Object arg) { - if (checkNeedsPing()) { - postMessage(new PingRequest()); - } - } - }, 5000L, null, true); } /** @@ -120,8 +109,6 @@ public class Communicator return; } - stopPingInterval(); - // post a logoff message postMessage(new LogoffRequest()); @@ -236,9 +223,6 @@ public class Communicator // clear out our reader reference _reader = null; - // in case we haven't already done it. - stopPingInterval(); - if (_writer == null) { // there's no writer during authentication, so we may be // responsible for closing the socket channel @@ -331,6 +315,14 @@ public class Communicator updateWriteStamp(); } + /** + * Returns the time at which we last sent a packet to the server. + */ + protected synchronized long getLastWrite () + { + return _lastWrite; + } + /** * Makes a note of the time at which we last communicated with the * server. @@ -340,27 +332,6 @@ public class Communicator _lastWrite = System.currentTimeMillis(); } - /** - * Checks to see if we need to send a ping to the server because we - * haven't communicated in sufficiently long. - */ - protected synchronized boolean checkNeedsPing () - { - long now = System.currentTimeMillis(); - return (now - _lastWrite > PingRequest.PING_INTERVAL); - } - - /** - * Stops our ping interval. - */ - protected void stopPingInterval () - { - if (_piid != -1) { - IntervalManager.remove(_piid); - _piid = -1; - } - } - /** * Reads a new message from the socket (blocking until a message has * arrived). @@ -600,7 +571,6 @@ public class Communicator protected SocketChannel _channel; protected Queue _msgq = new Queue(); - protected int _piid = -1; protected long _lastWrite; /** We use this to frame our upstream messages. */