From 12c628bcae07285880fd7a91282e1aaf241d950e Mon Sep 17 00:00:00 2001 From: nathan Date: Wed, 8 Nov 2017 15:53:22 +0100 Subject: [PATCH] More clearly specified TCP/UDP port requirements. Code polish --- src/dorkbox/network/Client.java | 34 ++++---- src/dorkbox/network/Configuration.java | 8 +- src/dorkbox/network/Server.java | 86 ++++++++++++++++--- .../network/connection/EndPointBase.java | 22 ++--- .../network/connection/EndPointClient.java | 4 +- .../network/connection/EndPointServer.java | 4 +- 6 files changed, 112 insertions(+), 46 deletions(-) diff --git a/src/dorkbox/network/Client.java b/src/dorkbox/network/Client.java index 2eed95d1..995178d2 100644 --- a/src/dorkbox/network/Client.java +++ b/src/dorkbox/network/Client.java @@ -95,21 +95,21 @@ class Client extends EndPointClient implements Connecti */ @SuppressWarnings("AutoBoxing") public - Client(final Configuration options) throws InitializationException, SecurityException, IOException { - super(options); + Client(final Configuration config) throws InitializationException, SecurityException, IOException { + super(config); String threadName = Client.class.getSimpleName(); Logger logger2 = this.logger; - if (options.localChannelName != null && (options.tcpPort > 0 || options.udpPort > 0 || options.host != null) || - options.localChannelName == null && (options.tcpPort == 0 || options.udpPort == 0 || options.host == null)) { + if (config.localChannelName != null && (config.tcpPort > 0 || config.udpPort > 0 || config.host != null) || + config.localChannelName == null && (config.tcpPort == 0 || config.udpPort == 0 || config.host == null)) { String msg = threadName + " Local channel use and TCP/UDP use are MUTUALLY exclusive. Unable to determine intent."; logger2.error(msg); throw new IllegalArgumentException(msg); } - localChannelName = options.localChannelName; - hostName = options.host; + localChannelName = config.localChannelName; + hostName = config.host; boolean isAndroid = PlatformDependent.isAndroid(); @@ -130,33 +130,33 @@ class Client extends EndPointClient implements Connecti manageForShutdown(boss); - if (options.localChannelName != null && options.tcpPort < 0 && options.udpPort < 0) { + if (config.localChannelName != null && config.tcpPort <= 0 && config.udpPort <= 0) { // no networked bootstraps. LOCAL connection only Bootstrap localBootstrap = new Bootstrap(); - this.bootstraps.add(new BootstrapWrapper("LOCAL", options.localChannelName, -1, localBootstrap)); + this.bootstraps.add(new BootstrapWrapper("LOCAL", config.localChannelName, -1, localBootstrap)); EventLoopGroup localBoss = new DefaultEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-LOCAL", threadGroup)); localBootstrap.group(localBoss) .channel(LocalChannel.class) - .remoteAddress(new LocalAddress(options.localChannelName)) + .remoteAddress(new LocalAddress(config.localChannelName)) .handler(new RegistrationLocalHandlerClient(threadName, registrationWrapper)); manageForShutdown(localBoss); } else { - if (options.host == null) { + if (config.host == null) { throw new IllegalArgumentException("You must define what host you want to connect to."); } - if (options.tcpPort < 0 && options.udpPort < 0) { + if (config.tcpPort <= 0 && config.udpPort <= 0) { throw new IllegalArgumentException("You must define what port you want to connect to."); } - if (options.tcpPort > 0) { + if (config.tcpPort > 0) { Bootstrap tcpBootstrap = new Bootstrap(); - this.bootstraps.add(new BootstrapWrapper("TCP", options.host, options.tcpPort, tcpBootstrap)); + this.bootstraps.add(new BootstrapWrapper("TCP", config.host, config.tcpPort, tcpBootstrap)); if (isAndroid) { // android ONLY supports OIO (not NIO) @@ -173,7 +173,7 @@ class Client extends EndPointClient implements Connecti tcpBootstrap.group(boss) .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT) .option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(WRITE_BUFF_LOW, WRITE_BUFF_HIGH)) - .remoteAddress(options.host, options.tcpPort) + .remoteAddress(config.host, config.tcpPort) .handler(new RegistrationRemoteHandlerClientTCP(threadName, registrationWrapper, serializationManager)); @@ -184,9 +184,9 @@ class Client extends EndPointClient implements Connecti } - if (options.udpPort > 0) { + if (config.udpPort > 0) { Bootstrap udpBootstrap = new Bootstrap(); - this.bootstraps.add(new BootstrapWrapper("UDP", options.host, options.udpPort, udpBootstrap)); + this.bootstraps.add(new BootstrapWrapper("UDP", config.host, config.udpPort, udpBootstrap)); if (isAndroid) { // android ONLY supports OIO (not NIO) @@ -204,7 +204,7 @@ class Client extends EndPointClient implements Connecti .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT) .option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(WRITE_BUFF_LOW, WRITE_BUFF_HIGH)) .localAddress(new InetSocketAddress(0)) // bind to wildcard - .remoteAddress(new InetSocketAddress(options.host, options.udpPort)) + .remoteAddress(new InetSocketAddress(config.host, config.udpPort)) .handler(new RegistrationRemoteHandlerClientUDP(threadName, registrationWrapper, serializationManager)); diff --git a/src/dorkbox/network/Configuration.java b/src/dorkbox/network/Configuration.java index 50512d89..1fe80081 100644 --- a/src/dorkbox/network/Configuration.java +++ b/src/dorkbox/network/Configuration.java @@ -30,15 +30,19 @@ class Configuration { /** * Specify the TCP port to use. The server will listen on this port, the client will connect to it. + *

+ * Must be > 0 to be used */ - public int tcpPort = -1; + public int tcpPort = 0; /** * Specify the UDP port to use. The server will listen on this port, the client will connect to it. *

+ * Must be > 0 to be used + *

* UDP requires TCP to handshake */ - public int udpPort = -1; + public int udpPort = 0; /** * Specify the local channel name to use, if the default is not wanted. diff --git a/src/dorkbox/network/Server.java b/src/dorkbox/network/Server.java index 76e46907..e51a2710 100644 --- a/src/dorkbox/network/Server.java +++ b/src/dorkbox/network/Server.java @@ -16,7 +16,7 @@ package dorkbox.network; import java.io.IOException; -import java.util.concurrent.CountDownLatch; +import java.net.Socket; import org.slf4j.Logger; @@ -59,6 +59,8 @@ import io.netty.channel.socket.oio.OioServerSocketChannel; */ public class Server extends EndPointServer { + + /** * Gets the version number. */ @@ -84,7 +86,8 @@ class Server extends EndPointServer { private final String localChannelName; private final String hostName; - private final CountDownLatch blockUntilDone = new CountDownLatch(1); + private volatile boolean isRunning = false; + /** * Starts a LOCAL only server, with the default serialization scheme. @@ -99,21 +102,21 @@ class Server extends EndPointServer { */ @SuppressWarnings("AutoBoxing") public - Server(Configuration options) throws InitializationException, SecurityException, IOException { + Server(Configuration config) throws InitializationException, SecurityException, IOException { // watch-out for serialization... it can be NULL incoming. The EndPoint (superclass) sets it, if null, so // you have to make sure to use this.serialization - super(options); + super(config); - tcpPort = options.tcpPort; - udpPort = options.udpPort; + tcpPort = config.tcpPort; + udpPort = config.udpPort; - localChannelName = options.localChannelName; + localChannelName = config.localChannelName; - if (options.host == null) { + if (config.host == null) { hostName = "0.0.0.0"; } else { - hostName = options.host; + hostName = config.host; } @@ -213,7 +216,7 @@ class Server extends EndPointServer { serializationManager)); // have to check options.host for null. we don't bind to 0.0.0.0, we bind to "null" to get the "any" address! - if (options.host != null) { + if (config.host != null) { tcpBootstrap.localAddress(hostName, tcpPort); } else { @@ -351,8 +354,9 @@ class Server extends EndPointServer { future = udpBootstrap.bind(); future.await(); } catch (Exception e) { - String errorMessage = stopWithErrorMessage(logger2, "Could not bind to address " + hostName + " UDP port " + - udpPort + " on the server.", + String errorMessage = stopWithErrorMessage(logger2, + "Could not bind to address " + hostName + " UDP port " + udpPort + + " on the server.", e); throw new IllegalArgumentException(errorMessage); } @@ -369,11 +373,69 @@ class Server extends EndPointServer { manageForShutdown(future); } + isRunning = true; + // we now BLOCK until the stop method is called. // if we want to continue running code in the server, bind should be called in a separate, non-daemon thread. if (blockUntilTerminate) { waitForShutdown(); } } + + @Override + protected + void stopExtraActions() { + isRunning = false; + } + + /** + * @return true if this server has successfully bound to an IP address and is running + */ + public + boolean isRunning() { + return isRunning; + } + + /** + * Checks to see if different server (using the specified configuration) is already running. This will check across JVMs by checking the + * network socket directly, and assumes that if the port is in use and answers, then the server is "running". This does not try to + * authenticate or validate the connection. + *

+ * This does not check local-channels (which are intra-JVM only) or UDP connections. + *

+ * + * @return true if the configuration matches and can connect (but not verify) to the TCP control socket. + */ + public static + boolean isRunning(Configuration config) { + String host = config.host; + + // for us, we want a "null" host to connect to the "any" interface. + if (host == null) { + host = "0.0.0.0"; + } + + if (config.tcpPort == 0) { + return false; + } + + Socket sock = null; + + // since we check the socket, if we cannot connect to a socket, then we're done. + try { + sock = new Socket(host, config.tcpPort); + // if we can connect to the socket, it means that we are already running. + return sock.isConnected(); + } catch (Exception ignored) { + if (sock != null) { + try { + sock.close(); + } catch (IOException ignored2) { + } + } + } + + return false; + } } diff --git a/src/dorkbox/network/connection/EndPointBase.java b/src/dorkbox/network/connection/EndPointBase.java index 9706b084..524d584a 100644 --- a/src/dorkbox/network/connection/EndPointBase.java +++ b/src/dorkbox/network/connection/EndPointBase.java @@ -115,32 +115,32 @@ class EndPointBase extends EndPoint { /** * @param type this is either "Client" or "Server", depending on who is creating this endpoint. - * @param options these are the specific connection options + * @param config these are the specific connection options * * @throws InitializationException * @throws SecurityException */ @SuppressWarnings({"unchecked", "rawtypes"}) public - EndPointBase(Class type, final Configuration options) throws InitializationException, SecurityException, IOException { + EndPointBase(Class type, final Configuration config) throws InitializationException, SecurityException, IOException { super(type); // make sure that 'localhost' is ALWAYS our specific loopback IP address - if (options.host != null && (options.host.equals("localhost") || options.host.startsWith("127."))) { + if (config.host != null && (config.host.equals("localhost") || config.host.startsWith("127."))) { // localhost IP might not always be 127.0.0.1 - options.host = NetUtil.LOCALHOST.getHostAddress(); + config.host = NetUtil.LOCALHOST.getHostAddress(); } // serialization stuff - if (options.serialization != null) { - this.serializationManager = options.serialization; + if (config.serialization != null) { + this.serializationManager = config.serialization; } else { this.serializationManager = CryptoSerializationManager.DEFAULT(); } // setup our RMI serialization managers. Can only be called once rmiEnabled = serializationManager.initRmiSerialization(); - rmiExecutor = options.rmiExecutor; + rmiExecutor = config.rmiExecutor; // The registration wrapper permits the registration process to access protected/package fields/methods, that we don't want @@ -153,17 +153,17 @@ class EndPointBase extends EndPoint { // we have to be able to specify WHAT property store we want to use, since it can change! - if (options.settingsStore == null) { + if (config.settingsStore == null) { this.propertyStore = new PropertyStore(); } else { - this.propertyStore = options.settingsStore; + this.propertyStore = config.settingsStore; } this.propertyStore.init(this.serializationManager, null); // null it out, since it is sensitive! - options.settingsStore = null; + config.settingsStore = null; if (!(this.propertyStore instanceof NullSettingsStore)) { @@ -217,7 +217,7 @@ class EndPointBase extends EndPoint { if (this.rmiEnabled) { // these register the listener for registering a class implementation for RMI (internal use only) this.connectionManager.add(new RegisterRmiSystemListener()); - this.globalRmiBridge = new RmiBridge(logger, options.rmiExecutor, true); + this.globalRmiBridge = new RmiBridge(logger, config.rmiExecutor, true); } else { this.globalRmiBridge = null; diff --git a/src/dorkbox/network/connection/EndPointClient.java b/src/dorkbox/network/connection/EndPointClient.java index 0a92cee6..6d31e5f0 100644 --- a/src/dorkbox/network/connection/EndPointClient.java +++ b/src/dorkbox/network/connection/EndPointClient.java @@ -50,8 +50,8 @@ class EndPointClient extends EndPointBase implements Ru public - EndPointClient(Configuration options) throws InitializationException, SecurityException, IOException { - super(Client.class, options); + EndPointClient(Configuration config) throws InitializationException, SecurityException, IOException { + super(Client.class, config); } protected diff --git a/src/dorkbox/network/connection/EndPointServer.java b/src/dorkbox/network/connection/EndPointServer.java index 0ba54a19..f3599a63 100644 --- a/src/dorkbox/network/connection/EndPointServer.java +++ b/src/dorkbox/network/connection/EndPointServer.java @@ -30,8 +30,8 @@ public class EndPointServer extends EndPointBase { public - EndPointServer(final Configuration options) throws InitializationException, SecurityException, IOException { - super(Server.class, options); + EndPointServer(final Configuration config) throws InitializationException, SecurityException, IOException { + super(Server.class, config); } /**