More clearly specified TCP/UDP port requirements. Code polish

This commit is contained in:
nathan 2017-11-08 15:53:22 +01:00
parent c33a086465
commit 12c628bcae
6 changed files with 112 additions and 46 deletions

View File

@ -95,21 +95,21 @@ class Client<C extends Connection> extends EndPointClient<C> implements Connecti
*/ */
@SuppressWarnings("AutoBoxing") @SuppressWarnings("AutoBoxing")
public public
Client(final Configuration options) throws InitializationException, SecurityException, IOException { Client(final Configuration config) throws InitializationException, SecurityException, IOException {
super(options); super(config);
String threadName = Client.class.getSimpleName(); String threadName = Client.class.getSimpleName();
Logger logger2 = this.logger; Logger logger2 = this.logger;
if (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) ||
options.localChannelName == null && (options.tcpPort == 0 || options.udpPort == 0 || options.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."; String msg = threadName + " Local channel use and TCP/UDP use are MUTUALLY exclusive. Unable to determine intent.";
logger2.error(msg); logger2.error(msg);
throw new IllegalArgumentException(msg); throw new IllegalArgumentException(msg);
} }
localChannelName = options.localChannelName; localChannelName = config.localChannelName;
hostName = options.host; hostName = config.host;
boolean isAndroid = PlatformDependent.isAndroid(); boolean isAndroid = PlatformDependent.isAndroid();
@ -130,33 +130,33 @@ class Client<C extends Connection> extends EndPointClient<C> implements Connecti
manageForShutdown(boss); 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 // no networked bootstraps. LOCAL connection only
Bootstrap localBootstrap = new Bootstrap(); 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", EventLoopGroup localBoss = new DefaultEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-LOCAL",
threadGroup)); threadGroup));
localBootstrap.group(localBoss) localBootstrap.group(localBoss)
.channel(LocalChannel.class) .channel(LocalChannel.class)
.remoteAddress(new LocalAddress(options.localChannelName)) .remoteAddress(new LocalAddress(config.localChannelName))
.handler(new RegistrationLocalHandlerClient<C>(threadName, registrationWrapper)); .handler(new RegistrationLocalHandlerClient<C>(threadName, registrationWrapper));
manageForShutdown(localBoss); manageForShutdown(localBoss);
} }
else { else {
if (options.host == null) { if (config.host == null) {
throw new IllegalArgumentException("You must define what host you want to connect to."); 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."); 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(); 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) { if (isAndroid) {
// android ONLY supports OIO (not NIO) // android ONLY supports OIO (not NIO)
@ -173,7 +173,7 @@ class Client<C extends Connection> extends EndPointClient<C> implements Connecti
tcpBootstrap.group(boss) tcpBootstrap.group(boss)
.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT) .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(WRITE_BUFF_LOW, WRITE_BUFF_HIGH)) .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<C>(threadName, .handler(new RegistrationRemoteHandlerClientTCP<C>(threadName,
registrationWrapper, registrationWrapper,
serializationManager)); serializationManager));
@ -184,9 +184,9 @@ class Client<C extends Connection> extends EndPointClient<C> implements Connecti
} }
if (options.udpPort > 0) { if (config.udpPort > 0) {
Bootstrap udpBootstrap = new Bootstrap(); 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) { if (isAndroid) {
// android ONLY supports OIO (not NIO) // android ONLY supports OIO (not NIO)
@ -204,7 +204,7 @@ class Client<C extends Connection> extends EndPointClient<C> implements Connecti
.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT) .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(WRITE_BUFF_LOW, WRITE_BUFF_HIGH)) .option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(WRITE_BUFF_LOW, WRITE_BUFF_HIGH))
.localAddress(new InetSocketAddress(0)) // bind to wildcard .localAddress(new InetSocketAddress(0)) // bind to wildcard
.remoteAddress(new InetSocketAddress(options.host, options.udpPort)) .remoteAddress(new InetSocketAddress(config.host, config.udpPort))
.handler(new RegistrationRemoteHandlerClientUDP<C>(threadName, .handler(new RegistrationRemoteHandlerClientUDP<C>(threadName,
registrationWrapper, registrationWrapper,
serializationManager)); serializationManager));

View File

@ -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. * Specify the TCP port to use. The server will listen on this port, the client will connect to it.
* <p>
* 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. * Specify the UDP port to use. The server will listen on this port, the client will connect to it.
* <p> * <p>
* Must be > 0 to be used
* <p>
* UDP requires TCP to handshake * 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. * Specify the local channel name to use, if the default is not wanted.

View File

@ -16,7 +16,7 @@
package dorkbox.network; package dorkbox.network;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.CountDownLatch; import java.net.Socket;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -59,6 +59,8 @@ import io.netty.channel.socket.oio.OioServerSocketChannel;
*/ */
public public
class Server<C extends Connection> extends EndPointServer<C> { class Server<C extends Connection> extends EndPointServer<C> {
/** /**
* Gets the version number. * Gets the version number.
*/ */
@ -84,7 +86,8 @@ class Server<C extends Connection> extends EndPointServer<C> {
private final String localChannelName; private final String localChannelName;
private final String hostName; private final String hostName;
private final CountDownLatch blockUntilDone = new CountDownLatch(1); private volatile boolean isRunning = false;
/** /**
* Starts a LOCAL <b>only</b> server, with the default serialization scheme. * Starts a LOCAL <b>only</b> server, with the default serialization scheme.
@ -99,21 +102,21 @@ class Server<C extends Connection> extends EndPointServer<C> {
*/ */
@SuppressWarnings("AutoBoxing") @SuppressWarnings("AutoBoxing")
public 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 // 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 // you have to make sure to use this.serialization
super(options); super(config);
tcpPort = options.tcpPort; tcpPort = config.tcpPort;
udpPort = options.udpPort; udpPort = config.udpPort;
localChannelName = options.localChannelName; localChannelName = config.localChannelName;
if (options.host == null) { if (config.host == null) {
hostName = "0.0.0.0"; hostName = "0.0.0.0";
} }
else { else {
hostName = options.host; hostName = config.host;
} }
@ -213,7 +216,7 @@ class Server<C extends Connection> extends EndPointServer<C> {
serializationManager)); 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! // 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); tcpBootstrap.localAddress(hostName, tcpPort);
} }
else { else {
@ -351,8 +354,9 @@ class Server<C extends Connection> extends EndPointServer<C> {
future = udpBootstrap.bind(); future = udpBootstrap.bind();
future.await(); future.await();
} catch (Exception e) { } catch (Exception e) {
String errorMessage = stopWithErrorMessage(logger2, "Could not bind to address " + hostName + " UDP port " + String errorMessage = stopWithErrorMessage(logger2,
udpPort + " on the server.", "Could not bind to address " + hostName + " UDP port " + udpPort +
" on the server.",
e); e);
throw new IllegalArgumentException(errorMessage); throw new IllegalArgumentException(errorMessage);
} }
@ -369,11 +373,69 @@ class Server<C extends Connection> extends EndPointServer<C> {
manageForShutdown(future); manageForShutdown(future);
} }
isRunning = true;
// we now BLOCK until the stop method is called. // 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 we want to continue running code in the server, bind should be called in a separate, non-daemon thread.
if (blockUntilTerminate) { if (blockUntilTerminate) {
waitForShutdown(); 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.
* <p>
* This does not check local-channels (which are intra-JVM only) or UDP connections.
* </p>
*
* @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;
}
} }

View File

@ -115,32 +115,32 @@ class EndPointBase<C extends Connection> extends EndPoint {
/** /**
* @param type this is either "Client" or "Server", depending on who is creating this 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 InitializationException
* @throws SecurityException * @throws SecurityException
*/ */
@SuppressWarnings({"unchecked", "rawtypes"}) @SuppressWarnings({"unchecked", "rawtypes"})
public public
EndPointBase(Class<? extends EndPointBase> type, final Configuration options) throws InitializationException, SecurityException, IOException { EndPointBase(Class<? extends EndPointBase> type, final Configuration config) throws InitializationException, SecurityException, IOException {
super(type); super(type);
// make sure that 'localhost' is ALWAYS our specific loopback IP address // 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 // localhost IP might not always be 127.0.0.1
options.host = NetUtil.LOCALHOST.getHostAddress(); config.host = NetUtil.LOCALHOST.getHostAddress();
} }
// serialization stuff // serialization stuff
if (options.serialization != null) { if (config.serialization != null) {
this.serializationManager = options.serialization; this.serializationManager = config.serialization;
} else { } else {
this.serializationManager = CryptoSerializationManager.DEFAULT(); this.serializationManager = CryptoSerializationManager.DEFAULT();
} }
// setup our RMI serialization managers. Can only be called once // setup our RMI serialization managers. Can only be called once
rmiEnabled = serializationManager.initRmiSerialization(); 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 // The registration wrapper permits the registration process to access protected/package fields/methods, that we don't want
@ -153,17 +153,17 @@ class EndPointBase<C extends Connection> extends EndPoint {
// we have to be able to specify WHAT property store we want to use, since it can change! // 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(); this.propertyStore = new PropertyStore();
} }
else { else {
this.propertyStore = options.settingsStore; this.propertyStore = config.settingsStore;
} }
this.propertyStore.init(this.serializationManager, null); this.propertyStore.init(this.serializationManager, null);
// null it out, since it is sensitive! // null it out, since it is sensitive!
options.settingsStore = null; config.settingsStore = null;
if (!(this.propertyStore instanceof NullSettingsStore)) { if (!(this.propertyStore instanceof NullSettingsStore)) {
@ -217,7 +217,7 @@ class EndPointBase<C extends Connection> extends EndPoint {
if (this.rmiEnabled) { if (this.rmiEnabled) {
// these register the listener for registering a class implementation for RMI (internal use only) // these register the listener for registering a class implementation for RMI (internal use only)
this.connectionManager.add(new RegisterRmiSystemListener()); this.connectionManager.add(new RegisterRmiSystemListener());
this.globalRmiBridge = new RmiBridge(logger, options.rmiExecutor, true); this.globalRmiBridge = new RmiBridge(logger, config.rmiExecutor, true);
} }
else { else {
this.globalRmiBridge = null; this.globalRmiBridge = null;

View File

@ -50,8 +50,8 @@ class EndPointClient<C extends Connection> extends EndPointBase<C> implements Ru
public public
EndPointClient(Configuration options) throws InitializationException, SecurityException, IOException { EndPointClient(Configuration config) throws InitializationException, SecurityException, IOException {
super(Client.class, options); super(Client.class, config);
} }
protected protected

View File

@ -30,8 +30,8 @@ public
class EndPointServer<C extends Connection> extends EndPointBase<C> { class EndPointServer<C extends Connection> extends EndPointBase<C> {
public public
EndPointServer(final Configuration options) throws InitializationException, SecurityException, IOException { EndPointServer(final Configuration config) throws InitializationException, SecurityException, IOException {
super(Server.class, options); super(Server.class, config);
} }
/** /**