From 2174852eaf1ccc3be50b2393f41a5ce9d9755c9d Mon Sep 17 00:00:00 2001 From: Richard Maw Date: Fri, 20 Dec 2024 19:13:13 +0000 Subject: [PATCH] Announce bound instead of configured server port If a port number of 0 is chosen then bind() will open a random port in an ephemeral range instead of the provided port number and this port can then be retrieved using getLocalPort(). This enables starting servers on arbitrary ports instead of preconfigured ones. Since only direct connections need to know the port number connections using the registry or LAN can use an ephemeral port number and the connection information tab can display the port direct connections should use. This requires a router with functioning UPnP instead of port forwarding though so is not univerally useful. --- .../simple/server/SocketServer.java | 13 +++++++++ .../clientserver/ConnectionFactory.java | 2 ++ .../rptools/maptool/server/MapToolServer.java | 28 ++++++++++++------- 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/clientserver/src/main/java/net/rptools/clientserver/simple/server/SocketServer.java b/clientserver/src/main/java/net/rptools/clientserver/simple/server/SocketServer.java index c7341d169f..4ed70b9dbb 100644 --- a/clientserver/src/main/java/net/rptools/clientserver/simple/server/SocketServer.java +++ b/clientserver/src/main/java/net/rptools/clientserver/simple/server/SocketServer.java @@ -39,6 +39,9 @@ public SocketServer(int port) { @Override public void start() throws IOException { var serverSocket = new ServerSocket(port); + if (serverSocket.getLocalPort() == -1) { + throw new AssertionError("Socket not bound yet"); + } // If the above throws, it will be as though we never started. socket = serverSocket; @@ -70,6 +73,16 @@ public String getError() { return null; } + /** Get the port of the socket the server is running on or -1. */ + public int getPort() { + // NOTE: We do not use this.port because the socket's bound port can be different + if (socket == null || socket.isClosed()) { + return -1; + } + + return socket.getLocalPort(); + } + //// // Threads private static class ListeningThread extends Thread { diff --git a/src/main/java/net/rptools/clientserver/ConnectionFactory.java b/src/main/java/net/rptools/clientserver/ConnectionFactory.java index ff9518ba7c..8cedbe399b 100644 --- a/src/main/java/net/rptools/clientserver/ConnectionFactory.java +++ b/src/main/java/net/rptools/clientserver/ConnectionFactory.java @@ -15,6 +15,7 @@ package net.rptools.clientserver; import java.awt.EventQueue; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import net.rptools.clientserver.simple.connection.Connection; import net.rptools.clientserver.simple.connection.SocketConnection; @@ -49,6 +50,7 @@ public void onLoginError() { }); } + @Nonnull public Server createServer(@Nullable ServerConfig config) { if (config == null) { return new NilServer(); diff --git a/src/main/java/net/rptools/maptool/server/MapToolServer.java b/src/main/java/net/rptools/maptool/server/MapToolServer.java index ef4729fb9b..dfa073c3ad 100644 --- a/src/main/java/net/rptools/maptool/server/MapToolServer.java +++ b/src/main/java/net/rptools/maptool/server/MapToolServer.java @@ -28,9 +28,12 @@ import net.rptools.clientserver.simple.DisconnectHandler; import net.rptools.clientserver.simple.MessageHandler; import net.rptools.clientserver.simple.connection.Connection; +import net.rptools.clientserver.simple.server.NilServer; import net.rptools.clientserver.simple.server.Router; import net.rptools.clientserver.simple.server.Server; import net.rptools.clientserver.simple.server.ServerObserver; +import net.rptools.clientserver.simple.server.SocketServer; +import net.rptools.clientserver.simple.server.WebRTCServer; import net.rptools.maptool.client.AppConstants; import net.rptools.maptool.client.MapTool; import net.rptools.maptool.client.MapToolRegistry; @@ -69,7 +72,7 @@ public enum State { } @Nonnull private final String serviceIdentifier; - private final Server server; + @Nonnull private final Server server; private final MessageHandler messageHandler; private final Router router; private final ServerConfig config; @@ -199,7 +202,11 @@ public String getName() { } public int getPort() { - return config == null ? -1 : config.getPort(); + return switch (server) { + case NilServer s -> -1; + case SocketServer s -> s.getPort(); + case WebRTCServer s -> -1; + }; } /** @@ -363,8 +370,8 @@ public void stop() { } // Close UPnP port mapping if used - if (useUPnP && config != null) { - int port = config.getPort(); + int port; + if (useUPnP && (port = getPort()) != -1) { UPnPUtil.closePort(port); } @@ -401,12 +408,13 @@ public void start() throws IOException { } // Use UPnP to open port in router - if (useUPnP && config != null) { + int port = getPort(); + if (useUPnP && port != -1) { MapTool.getFrame() .showFilledGlassPane( new StaticMessageDialog(I18N.getText("msg.info.server.upnp.discovering"))); try { - UPnPUtil.openPort(config.getPort()); + UPnPUtil.openPort(port); } finally { MapTool.getFrame().hideGlassPane(); } @@ -417,11 +425,11 @@ public void start() throws IOException { try { MapToolRegistry.RegisterResponse result = MapToolRegistry.getInstance() - .registerInstance(config.getServerName(), config.getPort(), config.getUseWebRTC()); + .registerInstance(config.getServerName(), port, config.getUseWebRTC()); if (result == MapToolRegistry.RegisterResponse.NAME_EXISTS) { MapTool.showError("msg.error.alreadyRegistered"); } else { - heartbeatThread = new HeartbeatThread(config.getPort()); + heartbeatThread = new HeartbeatThread(port); heartbeatThread.start(); } // TODO: I don't like this @@ -430,8 +438,8 @@ public void start() throws IOException { } } - if (serviceIdentifier != null && config != null) { - announcer = new ServiceAnnouncer(serviceIdentifier, config.getPort(), AppConstants.SERVICE_GROUP); + if (serviceIdentifier != null && port != -1) { + announcer = new ServiceAnnouncer(serviceIdentifier, port, AppConstants.SERVICE_GROUP); announcer.start(); }