diff --git a/common/src/main/java/com/taobao/arthas/common/SocketUtils.java b/common/src/main/java/com/taobao/arthas/common/SocketUtils.java index 28d2c5846..4d41b8600 100644 --- a/common/src/main/java/com/taobao/arthas/common/SocketUtils.java +++ b/common/src/main/java/com/taobao/arthas/common/SocketUtils.java @@ -3,10 +3,10 @@ package com.taobao.arthas.common; import java.net.InetAddress; import java.net.ServerSocket; import java.util.List; +import java.util.Random; import javax.net.ServerSocketFactory; - /** * * @author hengyunabc 2018-11-07 @@ -14,6 +14,20 @@ import javax.net.ServerSocketFactory; */ public class SocketUtils { + /** + * The default minimum value for port ranges used when finding an available + * socket port. + */ + public static final int PORT_RANGE_MIN = 1024; + + /** + * The default maximum value for port ranges used when finding an available + * socket port. + */ + public static final int PORT_RANGE_MAX = 65535; + + private static final Random random = new Random(System.currentTimeMillis()); + private SocketUtils() { } @@ -51,7 +65,7 @@ public class SocketUtils { public static boolean isTcpPortAvailable(int port) { try { ServerSocket serverSocket = ServerSocketFactory.getDefault().createServerSocket(port, 1, - InetAddress.getByName("localhost")); + InetAddress.getByName("localhost")); serverSocket.close(); return true; } catch (Exception ex) { @@ -59,4 +73,79 @@ public class SocketUtils { } } + /** + * Find an available TCP port randomly selected from the range + * [{@value #PORT_RANGE_MIN}, {@value #PORT_RANGE_MAX}]. + * + * @return an available TCP port number + * @throws IllegalStateException if no available port could be found + */ + public static int findAvailableTcpPort() { + return findAvailableTcpPort(PORT_RANGE_MIN); + } + + /** + * Find an available TCP port randomly selected from the range [{@code minPort}, + * {@value #PORT_RANGE_MAX}]. + * + * @param minPort the minimum port number + * @return an available TCP port number + * @throws IllegalStateException if no available port could be found + */ + public static int findAvailableTcpPort(int minPort) { + return findAvailableTcpPort(minPort, PORT_RANGE_MAX); + } + + /** + * Find an available TCP port randomly selected from the range [{@code minPort}, + * {@code maxPort}]. + * + * @param minPort the minimum port number + * @param maxPort the maximum port number + * @return an available TCP port number + * @throws IllegalStateException if no available port could be found + */ + public static int findAvailableTcpPort(int minPort, int maxPort) { + return findAvailablePort(minPort, maxPort); + } + + /** + * Find an available port for this {@code SocketType}, randomly selected from + * the range [{@code minPort}, {@code maxPort}]. + * + * @param minPort the minimum port number + * @param maxPort the maximum port number + * @return an available port number for this socket type + * @throws IllegalStateException if no available port could be found + */ + private static int findAvailablePort(int minPort, int maxPort) { + + int portRange = maxPort - minPort; + int candidatePort; + int searchCounter = 0; + do { + if (searchCounter > portRange) { + throw new IllegalStateException( + String.format("Could not find an available tcp port in the range [%d, %d] after %d attempts", + minPort, maxPort, searchCounter)); + } + candidatePort = findRandomPort(minPort, maxPort); + searchCounter++; + } while (!isTcpPortAvailable(candidatePort)); + + return candidatePort; + } + + /** + * Find a pseudo-random port number within the range [{@code minPort}, + * {@code maxPort}]. + * + * @param minPort the minimum port number + * @param maxPort the maximum port number + * @return a random port number within the specified range + */ + private static int findRandomPort(int minPort, int maxPort) { + int portRange = maxPort - minPort; + return minPort + random.nextInt(portRange + 1); + } } diff --git a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java index e8dd07338..9339981ed 100644 --- a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java +++ b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java @@ -27,6 +27,7 @@ import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; import com.alibaba.arthas.tunnel.client.TunnelClient; import com.taobao.arthas.common.AnsiLog; import com.taobao.arthas.common.PidUtils; +import com.taobao.arthas.common.SocketUtils; import com.taobao.arthas.core.advisor.TransformerManager; import com.taobao.arthas.core.command.BuiltinCommandPack; import com.taobao.arthas.core.command.view.ResultViewResolver; @@ -274,6 +275,18 @@ public class ArthasBootstrap { throw new IllegalStateException("already bind"); } + // init random port + if (configure.getTelnetPort() == 0) { + int newTelnetPort = SocketUtils.findAvailableTcpPort(); + configure.setTelnetPort(newTelnetPort); + logger().info("generate random telnet port: " + newTelnetPort); + } + if (configure.getHttpPort() == 0) { + int newHttpPort = SocketUtils.findAvailableTcpPort(); + configure.setHttpPort(newHttpPort); + logger().info("generate random http port: " + newHttpPort); + } + String agentId = null; try { if (configure.getTunnelServer() != null && configure.getHttpPort() > 0) {