From 77adfc15eceafdd106281d229c38d1ec15e8a448 Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Tue, 30 May 2006 22:12:15 +0000 Subject: [PATCH] Revamped the basic Presents client to support attempts to connect to the server on multiple ports, falling back from one to the next as appropriate. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4158 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- .../threerings/micasa/client/LogonPanel.java | 2 +- .../threerings/micasa/client/MiCasaApp.java | 25 ++++-------- .../micasa/client/MiCasaApplet.java | 4 +- .../micasa/client/MiCasaClient.java | 2 +- .../micasa/server/MiCasaServer.java | 17 +-------- .../micasa/simulator/client/SimulatorApp.java | 16 +------- .../threerings/presents/client/Client.java | 33 +++++++++++----- .../presents/client/ClientObserver.java | 10 ++++- .../presents/client/Communicator.java | 38 ++++++++++++++----- .../presents/client/LogonException.java | 32 +++++++++++++++- .../threerings/presents/data/AuthCodes.java | 6 ++- .../presents/server/PresentsServer.java | 2 +- .../threerings/crowd/client/JabberApp.java | 24 ++++-------- .../threerings/crowd/client/JabberClient.java | 2 +- .../threerings/crowd/client/TestClient.java | 2 +- .../com/threerings/jme/client/JabberApp.java | 21 +++------- .../threerings/jme/client/JabberClient.java | 2 +- .../presents/client/TestClient.java | 2 +- 18 files changed, 128 insertions(+), 112 deletions(-) diff --git a/src/java/com/threerings/micasa/client/LogonPanel.java b/src/java/com/threerings/micasa/client/LogonPanel.java index 3034295c9..38784f3be 100644 --- a/src/java/com/threerings/micasa/client/LogonPanel.java +++ b/src/java/com/threerings/micasa/client/LogonPanel.java @@ -205,7 +205,7 @@ public class LogonPanel extends JPanel String password = new String(_password.getPassword()).trim(); String server = _ctx.getClient().getHostname(); - int port = _ctx.getClient().getPort(); + int port = _ctx.getClient().getPorts()[0]; String msg = MessageBundle.tcompose("m.logging_on", server, String.valueOf(port)); _status.append(_msgs.xlate(msg) + "\n"); diff --git a/src/java/com/threerings/micasa/client/MiCasaApp.java b/src/java/com/threerings/micasa/client/MiCasaApp.java index 1a7678841..e81b34381 100644 --- a/src/java/com/threerings/micasa/client/MiCasaApp.java +++ b/src/java/com/threerings/micasa/client/MiCasaApp.java @@ -1,5 +1,5 @@ // -// $Id: MiCasaApp.java,v 1.15 2004/08/27 02:12:49 mdb Exp $ +// $Id$ // // Narya library - tools for developing networked games // Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved @@ -67,7 +67,7 @@ public class MiCasaApp _client.init(_frame); } - public void run (String server, int port, String username, String password) + public void run (String server, String username, String password) { // position everything and show the frame _frame.setSize(800, 600); @@ -76,9 +76,8 @@ public class MiCasaApp Client client = _client.getContext().getClient(); - // pass them on to the client - Log.info("Using [server=" + server + ", port=" + port + "]."); - client.setServer(server, port); + Log.info("Using [server=" + server + "."); + client.setServer(server, Client.DEFAULT_SERVER_PORTS); // configure the client with some credentials and logon if (username != null && password != null) { @@ -95,18 +94,8 @@ public class MiCasaApp if (args.length > 0) { server = args[0]; } - - int port = Client.DEFAULT_SERVER_PORT; - if (args.length > 1) { - try { - port = Integer.parseInt(args[1]); - } catch (NumberFormatException nfe) { - Log.warning("Invalid port specification '" + args[1] + "'."); - } - } - - String username = (args.length > 2) ? args[2] : null; - String password = (args.length > 3) ? args[3] : null; + String username = (args.length > 1) ? args[1] : null; + String password = (args.length > 2) ? args[2] : null; MiCasaApp app = new MiCasaApp(); try { @@ -118,7 +107,7 @@ public class MiCasaApp } // and run it - app.run(server, port, username, password); + app.run(server, username, password); } protected MiCasaClient _client; diff --git a/src/java/com/threerings/micasa/client/MiCasaApplet.java b/src/java/com/threerings/micasa/client/MiCasaApplet.java index 03a5cdcf4..f9ef0ff48 100644 --- a/src/java/com/threerings/micasa/client/MiCasaApplet.java +++ b/src/java/com/threerings/micasa/client/MiCasaApplet.java @@ -1,5 +1,5 @@ // -// $Id: MiCasaApplet.java,v 1.9 2004/08/27 02:12:49 mdb Exp $ +// $Id$ // // Narya library - tools for developing networked games // Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved @@ -59,7 +59,7 @@ public class MiCasaApplet extends Applet Client client = _client.getContext().getClient(); // indicate which server to which we should connect - client.setServer(server, Client.DEFAULT_SERVER_PORT); + client.setServer(server, Client.DEFAULT_SERVER_PORTS); // create and set our credentials client.setCredentials( diff --git a/src/java/com/threerings/micasa/client/MiCasaClient.java b/src/java/com/threerings/micasa/client/MiCasaClient.java index 3f7510abd..6a7304024 100644 --- a/src/java/com/threerings/micasa/client/MiCasaClient.java +++ b/src/java/com/threerings/micasa/client/MiCasaClient.java @@ -67,7 +67,7 @@ public class MiCasaClient createContextServices(); // for test purposes, hardcode the server info - _client.setServer("localhost", Client.DEFAULT_SERVER_PORT); + _client.setServer("localhost", Client.DEFAULT_SERVER_PORTS); // keep this for later _frame = frame; diff --git a/src/java/com/threerings/micasa/server/MiCasaServer.java b/src/java/com/threerings/micasa/server/MiCasaServer.java index c743b5bde..801572f8a 100644 --- a/src/java/com/threerings/micasa/server/MiCasaServer.java +++ b/src/java/com/threerings/micasa/server/MiCasaServer.java @@ -1,5 +1,5 @@ // -// $Id: MiCasaServer.java,v 1.9 2004/08/27 02:12:52 mdb Exp $ +// $Id$ // // Narya library - tools for developing networked games // Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved @@ -23,7 +23,6 @@ package com.threerings.micasa.server; import com.threerings.crowd.server.CrowdServer; import com.threerings.parlor.server.ParlorManager; -import com.threerings.presents.client.Client; import com.threerings.micasa.Log; import com.threerings.micasa.lobby.LobbyRegistry; @@ -61,20 +60,6 @@ public class MiCasaServer extends CrowdServer Log.info("MiCasa server initialized."); } - /** - * Returns the port on which the connection manager will listen for - * client connections. - */ - protected int getListenPort () - { - int port = Client.DEFAULT_SERVER_PORT; - try { - port = Integer.parseInt(System.getProperty("port")); - } catch (Exception e) { - } - return port; - } - public static void main (String[] args) { MiCasaServer server = new MiCasaServer(); diff --git a/src/java/com/threerings/micasa/simulator/client/SimulatorApp.java b/src/java/com/threerings/micasa/simulator/client/SimulatorApp.java index eb2989de0..7d65ff1a2 100644 --- a/src/java/com/threerings/micasa/simulator/client/SimulatorApp.java +++ b/src/java/com/threerings/micasa/simulator/client/SimulatorApp.java @@ -113,26 +113,14 @@ public class SimulatorApp { // configure and display the main frame JFrame frame = _frame.getFrame(); - // position everything and show the frame frame.setSize(800, 600); SwingUtil.centerWindow(frame); frame.setVisible(true); // start up the client Client client = _client.getParlorContext().getClient(); - - // obtain the port information from system properties - int port = Client.DEFAULT_SERVER_PORT; - String portstr = System.getProperty("port"); - if (portstr != null) { - try { - port = Integer.parseInt(portstr); - } catch (NumberFormatException nfe) { - Log.warning("Invalid port specification '" + portstr + "'."); - } - } - Log.info("Connecting to localhost:" + port + "."); - client.setServer("localhost", port); + Log.info("Connecting to localhost."); + client.setServer("localhost", Client.DEFAULT_SERVER_PORTS); // we want to exit when we logged off or failed to log on client.addClientObserver(new ClientAdapter() { diff --git a/src/java/com/threerings/presents/client/Client.java b/src/java/com/threerings/presents/client/Client.java index 4541bb3f1..4990298e4 100644 --- a/src/java/com/threerings/presents/client/Client.java +++ b/src/java/com/threerings/presents/client/Client.java @@ -42,9 +42,9 @@ import com.threerings.presents.net.PongResponse; */ public class Client { - /** The default port on which the server listens for client + /** The default ports on which the server listens for client * connections. */ - public static final int DEFAULT_SERVER_PORT = 47624; + public static final int[] DEFAULT_SERVER_PORTS = { 47624 }; /** * Constructs a client object with the supplied credentials and @@ -115,15 +115,15 @@ public class Client } /** - * Configures the client to communicate with the server on the - * supplied hostname/port combination. + * Configures the client to communicate with the server on the supplied + * hostname and set of ports (which will be tried in succession). * * @see #logon */ - public void setServer (String hostname, int port) + public void setServer (String hostname, int[] ports) { _hostname = hostname; - _port = port; + _ports = ports; } /** @@ -148,9 +148,9 @@ public class Client * Returns the port on which this client is currently configured to * connect to the server. */ - public int getPort () + public int[] getPorts () { - return _port; + return _ports; } /** @@ -519,6 +519,19 @@ public class Client } } + /** + * Called by the {@link Communicator} if it is experiencing trouble logging + * on but is still trying fallback strategies. + */ + protected void reportLogonTribulations (final LogonException cause) + { + _runQueue.postRunnable(new Runnable() { + public void run () { + notifyObservers(CLIENT_FAILED_TO_LOGON, cause); + } + }); + } + /** * Called by the invocation director when it successfully subscribes * to the client object immediately following logon. @@ -731,8 +744,8 @@ public class Client /** The game server host. */ protected String _hostname; - /** The port on which we connect to the game server. */ - protected int _port; + /** The ports on which we connect to the game server. */ + protected int[] _ports; /** Our list of client observers. */ protected ObserverList _observers = diff --git a/src/java/com/threerings/presents/client/ClientObserver.java b/src/java/com/threerings/presents/client/ClientObserver.java index bc20e4bb2..7996fb17a 100644 --- a/src/java/com/threerings/presents/client/ClientObserver.java +++ b/src/java/com/threerings/presents/client/ClientObserver.java @@ -1,5 +1,5 @@ // -// $Id: ClientObserver.java,v 1.9 2004/08/27 02:20:18 mdb Exp $ +// $Id$ // // Narya library - tools for developing networked games // Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved @@ -53,6 +53,14 @@ public interface ClientObserver extends SessionObserver * Called if anything fails during the logon attempt. This could be a * network failure, authentication failure or otherwise. The exception * provided will indicate the cause of the failure. + * + * @param cause an exception indicating the cause of the logon failure. + * Note: this may be a {@link LogonException} and if so, the + * caller must check {@link LogonException#isStillInProgress} to + * find out if the logon process has totally failed or if we are simply + * reporting intermediate status (we might be falling back to an + * alternative port or delaying our auto-retry attempt due to server + * overload). */ public void clientFailedToLogon (Client client, Exception cause); diff --git a/src/java/com/threerings/presents/client/Communicator.java b/src/java/com/threerings/presents/client/Communicator.java index d93fab00b..1698ad773 100644 --- a/src/java/com/threerings/presents/client/Communicator.java +++ b/src/java/com/threerings/presents/client/Communicator.java @@ -27,10 +27,12 @@ import java.io.InterruptedIOException; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; +import java.net.ConnectException; import java.net.InetAddress; import java.net.InetSocketAddress; import com.samskivert.swing.RuntimeAdjust; +import com.samskivert.util.IntListUtil; import com.samskivert.util.LoopingThread; import com.samskivert.util.Queue; import com.samskivert.util.StringUtil; @@ -41,6 +43,7 @@ import com.threerings.io.ObjectInputStream; import com.threerings.io.ObjectOutputStream; import com.threerings.presents.Log; +import com.threerings.presents.data.AuthCodes; import com.threerings.presents.dobj.DObjectManager; import com.threerings.presents.net.AuthRequest; import com.threerings.presents.net.AuthResponse; @@ -429,17 +432,34 @@ public class Communicator // look up the address of the target server InetAddress host = InetAddress.getByName(_client.getHostname()); - int port = _client.getPort(); - // establish a socket connection to said server - Log.debug("Connecting [host=" + host + ", port=" + port + "]."); - InetSocketAddress addr = new InetSocketAddress(host, port); - try { - _channel = SocketChannel.open(addr); - } catch (IOException ioe) { - Log.warning("Error opening [addr=" + addr + "]."); - throw ioe; // rethrow + // obtain the list of available ports on which to attempt our + // client connection and determine our preferred port + String pportKey = _client.getHostname() + ".preferred_port"; + int[] ports = _client.getPorts(); + int pport = PresentsPrefs.config.getValue(pportKey, ports[0]); + int ppidx = Math.max(0, IntListUtil.indexOf(ports, pport)); + + // try connecting on each of the ports in succession + for (int ii = 0; ii < ports.length; ii++) { + int port = ports[(ii+ppidx)%ports.length]; + Log.info("Connecting [host=" + host + ", port=" + port + "]."); + InetSocketAddress addr = new InetSocketAddress(host, port); + try { + _channel = SocketChannel.open(addr); + PresentsPrefs.config.setValue(pportKey, port); + break; + } catch (IOException ioe) { + if (ioe instanceof ConnectException && ii < ports.length) { + _client.reportLogonTribulations( + new LogonException( + AuthCodes.TRYING_NEXT_PORT, true)); + continue; // try the next port + } + throw ioe; + } } + _channel.configureBlocking(true); // our messages are framed (preceded by their length), so we diff --git a/src/java/com/threerings/presents/client/LogonException.java b/src/java/com/threerings/presents/client/LogonException.java index 3c0951503..d35e87c94 100644 --- a/src/java/com/threerings/presents/client/LogonException.java +++ b/src/java/com/threerings/presents/client/LogonException.java @@ -1,5 +1,5 @@ // -// $Id: LogonException.java,v 1.4 2004/08/27 02:20:18 mdb Exp $ +// $Id$ // // Narya library - tools for developing networked games // Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved @@ -33,6 +33,34 @@ public class LogonException extends Exception */ public LogonException (String code) { - super(code); + this(code, false); } + + /** + * Constructs a logon exception with the supplied logon failure code. + * + * @param stillInProgress indicates that the logon attempt has not totally + * failed, rather we are still trying but want to report that our initial + * attempt did not work and that we're falling back to alternative methods. + */ + public LogonException (String code, boolean stillInProgress) + { + super(code); + _stillInProgress = stillInProgress; + } + + /** + * Returns true if this exception is reporting an intermediate status + * rather than total logon failure. The client may be falling back to an + * alternative port or delaying an auto-retry attempt due to server + * overload. If this method returns true, the client should not + * allow additional calls to {@link Client#logon} as the current attempt is + * still in progress. + */ + public boolean isStillInProgress () + { + return _stillInProgress; + } + + protected boolean _stillInProgress; } diff --git a/src/java/com/threerings/presents/data/AuthCodes.java b/src/java/com/threerings/presents/data/AuthCodes.java index 39a87a46a..cc1ffc655 100644 --- a/src/java/com/threerings/presents/data/AuthCodes.java +++ b/src/java/com/threerings/presents/data/AuthCodes.java @@ -1,5 +1,5 @@ // -// $Id: AuthCodes.java,v 1.3 2004/08/27 02:20:19 mdb Exp $ +// $Id$ // // Narya library - tools for developing networked games // Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved @@ -39,4 +39,8 @@ public interface AuthCodes /** A code indicating that the server is not available at the moment. */ public static final String SERVER_UNAVAILABLE = "m.server_unavailable"; + + /** A code indicating that we failed to connect to the server on a port and + * are trying the next port in the list. */ + public static final String TRYING_NEXT_PORT = "m.trying_next_port"; } diff --git a/src/java/com/threerings/presents/server/PresentsServer.java b/src/java/com/threerings/presents/server/PresentsServer.java index 0bcf6bc3c..206bf3e43 100644 --- a/src/java/com/threerings/presents/server/PresentsServer.java +++ b/src/java/com/threerings/presents/server/PresentsServer.java @@ -153,7 +153,7 @@ public class PresentsServer */ protected int[] getListenPorts () { - return new int[] { Client.DEFAULT_SERVER_PORT }; + return Client.DEFAULT_SERVER_PORTS; } /** diff --git a/tests/src/java/com/threerings/crowd/client/JabberApp.java b/tests/src/java/com/threerings/crowd/client/JabberApp.java index a98b04a81..50ddcc021 100644 --- a/tests/src/java/com/threerings/crowd/client/JabberApp.java +++ b/tests/src/java/com/threerings/crowd/client/JabberApp.java @@ -50,8 +50,8 @@ public class JabberApp _client.init(_frame); } - public void run (String server, int port, String username, String password, - int roomId) + public void run ( + String server, String username, String password, int roomId) { // position everything and show the frame _frame.setSize(800, 600); @@ -62,8 +62,8 @@ public class JabberApp Client client = _client.getContext().getClient(); // pass them on to the client - Log.info("Using [server=" + server + ", port=" + port + "]."); - client.setServer(server, port); + Log.info("Using [server=" + server + "]."); + client.setServer(server, Client.DEFAULT_SERVER_PORTS); // configure the client with some credentials and logon if (username != null && password != null) { @@ -80,18 +80,8 @@ public class JabberApp if (args.length > 0) { server = args[0]; } - - int port = Client.DEFAULT_SERVER_PORT; - if (args.length > 1) { - try { - port = Integer.parseInt(args[1]); - } catch (NumberFormatException nfe) { - Log.warning("Invalid port specification '" + args[1] + "'."); - } - } - - String username = (args.length > 2) ? args[2] : null; - String password = (args.length > 3) ? args[3] : null; + String username = (args.length > 1) ? args[1] : null; + String password = (args.length > 2) ? args[2] : null; int roomId = 2; if (args.length > 4) { try { @@ -111,7 +101,7 @@ public class JabberApp } // and run it - app.run(server, port, username, password, roomId); + app.run(server, username, password, roomId); } protected JabberClient _client; diff --git a/tests/src/java/com/threerings/crowd/client/JabberClient.java b/tests/src/java/com/threerings/crowd/client/JabberClient.java index 5ffc171d5..f2b4c5244 100644 --- a/tests/src/java/com/threerings/crowd/client/JabberClient.java +++ b/tests/src/java/com/threerings/crowd/client/JabberClient.java @@ -63,7 +63,7 @@ public class JabberClient createContextServices(); // for test purposes, hardcode the server info - _client.setServer("localhost", Client.DEFAULT_SERVER_PORT); + _client.setServer("localhost", Client.DEFAULT_SERVER_PORTS); _client.addClientObserver(this); // keep this for later diff --git a/tests/src/java/com/threerings/crowd/client/TestClient.java b/tests/src/java/com/threerings/crowd/client/TestClient.java index 89ff526b7..9bf6e3667 100644 --- a/tests/src/java/com/threerings/crowd/client/TestClient.java +++ b/tests/src/java/com/threerings/crowd/client/TestClient.java @@ -55,7 +55,7 @@ public class TestClient _client.addClientObserver(this); // for test purposes, hardcode the server info - _client.setServer("localhost", Client.DEFAULT_SERVER_PORT); + _client.setServer("localhost", Client.DEFAULT_SERVER_PORTS); } public void run () diff --git a/tests/src/java/com/threerings/jme/client/JabberApp.java b/tests/src/java/com/threerings/jme/client/JabberApp.java index 663709519..ebb28092c 100644 --- a/tests/src/java/com/threerings/jme/client/JabberApp.java +++ b/tests/src/java/com/threerings/jme/client/JabberApp.java @@ -95,13 +95,13 @@ public class JabberApp extends JmeApp return true; } - public void run (String server, int port, String username, String password) + public void run (String server, String username, String password) { Client client = _client.getContext().getClient(); // pass them on to the client - Log.info("Using [server=" + server + ", port=" + port + "]."); - client.setServer(server, port); + Log.info("Using [server=" + server + "."); + client.setServer(server, Client.DEFAULT_SERVER_PORTS); // configure the client with some credentials and logon if (username != null && password != null) { @@ -136,15 +136,6 @@ public class JabberApp extends JmeApp server = args[0]; } - int port = Client.DEFAULT_SERVER_PORT; - if (args.length > 1) { - try { - port = Integer.parseInt(args[1]); - } catch (NumberFormatException nfe) { - Log.warning("Invalid port specification '" + args[1] + "'."); - } - } - // load up the default BUI stylesheet try { InputStream stin = JabberApp.class.getClassLoader(). @@ -157,12 +148,12 @@ public class JabberApp extends JmeApp System.exit(-1); } - String username = (args.length > 2) ? args[2] : null; - String password = (args.length > 3) ? args[3] : null; + String username = (args.length > 1) ? args[1] : null; + String password = (args.length > 2) ? args[2] : null; JabberApp app = new JabberApp(); app.init(); - app.run(server, port, username, password); + app.run(server, username, password); } protected JabberClient _client; diff --git a/tests/src/java/com/threerings/jme/client/JabberClient.java b/tests/src/java/com/threerings/jme/client/JabberClient.java index ad8817faf..2fc854f94 100644 --- a/tests/src/java/com/threerings/jme/client/JabberClient.java +++ b/tests/src/java/com/threerings/jme/client/JabberClient.java @@ -67,7 +67,7 @@ public class JabberClient createContextServices(app); // for test purposes, hardcode the server info - _client.setServer("localhost", Client.DEFAULT_SERVER_PORT); + _client.setServer("localhost", Client.DEFAULT_SERVER_PORTS); _client.addClientObserver(this); } diff --git a/tests/src/java/com/threerings/presents/client/TestClient.java b/tests/src/java/com/threerings/presents/client/TestClient.java index 589708334..0836db116 100644 --- a/tests/src/java/com/threerings/presents/client/TestClient.java +++ b/tests/src/java/com/threerings/presents/client/TestClient.java @@ -154,7 +154,7 @@ public class TestClient Client client = new Client(creds, tclient); tclient.setClient(client); client.addClientObserver(tclient); - client.setServer("localhost", Client.DEFAULT_SERVER_PORT); + client.setServer("localhost", Client.DEFAULT_SERVER_PORTS); client.logon(); // start up our event processing loop tclient.run();