如何查找可用端口?

222

我想启动一个监听端口的服务器。我可以明确指定端口并且它可以工作。但是我希望以自动方式查找可用端口。在这方面,我有两个问题。

  1. 应该在哪个端口范围内搜索可用端口号?(我使用了12345、12346和12347端口,它们都可以正常使用)。

  2. 如何确定给定的端口是否已被其他软件占用?


如果端口被其他软件占用,代码将抛出一个IOException。 - Cezar Terzian
19个回答

5
如果你的服务器启动,那么该套接字未被使用。
编辑:
类似这样:
ServerSocket s = null ;

try { 
    s = new ServerSocket( 0 ); 
} catch( IOException ioe ){
   for( int i = START; i < END ; i++ ) try {
        s = new ServerSocket( i );
    } catch( IOException ioe ){}
}
// At this point if s is null we are helpless
if( s == null ) {
    throw new IOException(
       Strings.format("Unable to open server in port range(%d-%d)",START,END));
}

不知道谁给你投了反对票,但我给你投了赞成票。你可以设置一个递归函数来尝试设置ServerSocket,如果出现IOException(或其他错误),则重复尝试直到成功获取端口。 - jonescb
我认为最好先检查端口是否可用,然后再尝试开始监听该端口。启动某些东西,然后发现存在问题,然后尝试解决这些问题似乎并不优雅。 - Roman
2
@Roman 说得没错,那确实更好,但是问题在于没有一种方法可以知道端口是否可用。如果“new ServerSocket(0)”对您不起作用,则可以使用以下替代方法。我认为有85%的可能性您最终会采纳我的建议。 - OscarRyz
这段代码会一直打开和泄漏“ServerSockets”,只保留最后一个成功的。它需要一个“break”语句。 - user207421

4
如果您想使用ServerSocket创建自己的服务器,您可以让它为您选择一个空闲端口:
  ServerSocket serverSocket = new ServerSocket(0);
  int port = serverSocket.getLocalPort();

其他服务器实现通常具有类似的支持。例如,Jetty 会选择一个空闲端口,除非您明确设置了它:
  Server server = new Server();
  ServerConnector connector = new ServerConnector(server);
  // don't call: connector.setPort(port);
  server.addConnector(connector);
  server.start();
  int port = connector.getLocalPort();

3

虽然可能对你帮助不大,但在我的(Ubuntu)机器上,我有一个/etc/services文件,其中至少列出了一些应用程序使用/保留的端口。这些是这些应用程序的标准端口。

不能保证这些正在运行,只是这些应用程序使用的默认端口(因此如果可能的话,您不应该使用它们)。

定义了略多于500个端口,约一半为UDP,一半为TCP。

这些文件使用IANA的信息制作而成,请参见IANA分配的端口号


有一个类似的(更完整,如果我没记错的话)列表作为nmap的一部分。+1 - rmeador

3
如果您正在使用Spring,Michail Nikolaev提供的答案是最简单和最清晰的,我认为应该得到赞同。为了方便起见,我将添加一个使用Springframework SocketUtils.findAvailableTcpPort()方法的内联示例:

int randomAvailablePort = SocketUtils.findAvailableTcpPort();

就这么简单,只需要一行代码 :) 当然,Utils类还提供许多其他有趣的方法,建议查看文档。


1
已经过时了! - RamPrakash

2
如果你正在使用Spring框架,最简单的方法是:
private Integer laancNotifyPort = SocketUtils.findAvailableTcpPort();

您还可以设置一个可接受的范围,它将在此范围内搜索:

private Integer laancNotifyPort = SocketUtils.findAvailableTcpPort(9090, 10090);

这是一个方便的方法,它抽象了复杂性,但在内部与此线程上的许多其他答案类似。


3
SocketUtils已被标记为Spring Boot(Spring版本?)2.6.X的弃用。 - khmarbaise

1

这里有很多使用ServerSocket的答案。我检查了Micronauts的实现,他们尝试将客户端套接字连接到本地端口,如果失败,则表示该端口已打开。对我来说,这具有优势,因为他们不会在测试中使用该端口。

他们的代码如下:

    public static boolean isTcpPortAvailable(int currentPort) {
        try (Socket socket = new Socket()) {
            socket.connect(new InetSocketAddress(InetAddress.getLocalHost(), currentPort), 20);
            return false;
        } catch (Throwable e) {
            return true;
        }
    }

请参考此处:https://github.com/micronaut-projects/micronaut-core/blob/3.4.x/core/src/main/java/io/micronaut/core/io/socket/SocketUtils.java

这是一个有趣的方法,尽管存在竞态条件的可能性。我可以测试一下哪个在我的设置上运行得更快,以降低由于竞态条件导致返回“错误结果”的概率。 - dragi

1
使用'ServerSocket'类,我们可以确定给定的端口是否正在使用中。ServerSocket提供了一个构造函数,它接受一个整数(即端口号)作为参数,并在该端口上初始化服务器套接字。如果ServerSocket抛出任何IO异常,则可以假定此端口已经在使用中。
以下代码片段用于获取所有可用端口。
for (int port = 1; port < 65535; port++) {
         try {
                  ServerSocket socket = new ServerSocket(port);
                  socket.close();
                  availablePorts.add(port);
         } catch (IOException e) {

         }
}

参考资料 link


1
很多人提到了Spring框架的实用工具,但如果你不使用Spring,这个工具的逻辑非常简单,比大多数答案都要好,因为它能给出真正随机的结果。 这里是完整的源代码
但是这里有一个简化的源代码,你可以直接在自己的项目中使用,而不需要添加Spring作为依赖。
public static int findAvailableTcpPort() {
    int minPort = 1024;
    int maxPort = 65535;
    int portRange = maxPort - minPort;
    int maxAttempts = 1000;
    int candidatePort;
    int searchCounter = 0;
    Random random = new Random(System.nanoTime());
    do {
        if (searchCounter > maxAttempts) {
            throw new IllegalStateException(String.format(
                "Could not find an available TCP port in the range [%d, %d] after %d attempts",
                minPort, maxPort, maxAttempts));
        }
        candidatePort = minPort + random.nextInt(portRange + 1);
        searchCounter++;
    } while (!isPortAvailable(candidatePort));

    return candidatePort;
}

private static boolean isPortAvailable(int port) {
    try {
        ServerSocket serverSocket = ServerSocketFactory.getDefault().createServerSocket(port, 1, InetAddress.getByName("localhost"));
        serverSocket.close();
        return true;
    } catch (Exception ex){
        return false;
    }
}

0

你可以请求ServerSocket为你找一个端口,然后关闭它:

private int getFreePort() {
        var freePort = 0;
        try (ServerSocket s = new ServerSocket(0)) {
            freePort = s.getLocalPort();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return freePort;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接