【问题标题】:How to find an available port?如何找到可用的端口?
【发布时间】:2011-02-10 03:32:34
【问题描述】:

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

  1. 我应该在哪个端口号范围内搜索? (我用了 12345、12346 和 12347 端口,没问题)。

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

【问题讨论】:

  • 如果端口被其他软件占用,代码会抛出 IOException

标签: java networking sockets port


【解决方案1】:

如果您不介意使用的端口,请将端口 0 指定给 ServerSocket constructor,它将侦听任何空闲端口。

ServerSocket s = new ServerSocket(0);
System.out.println("listening on port: " + s.getLocalPort());

如果您想使用一组特定的端口,那么最简单的方法可能是遍历它们直到一个工作。像这样的:

public ServerSocket create(int[] ports) throws IOException {
    for (int port : ports) {
        try {
            return new ServerSocket(port);
        } catch (IOException ex) {
            continue; // try next port
        }
    }

    // if the program gets here, no port in the range was found
    throw new IOException("no free port found");
}

可以这样使用:

try {
    ServerSocket s = create(new int[] { 3843, 4584, 4843 });
    System.out.println("listening on port: " + s.getLocalPort());
} catch (IOException ex) {
    System.err.println("no available ports");
}

【讨论】:

  • 使用new ServerSocket(0)时,要注意关闭它!基于javasourcecode.org/html/open-source/eclipse/eclipse-3.5.2/org/…,略改编于我的gist.github.com/3429822
  • @vorburger,以这种方式进行操作很容易出现竞争条件。最好立即在服务器套接字上监听,而不是打开它以找到一个空闲端口,再次关闭它,然后在空闲端口上再次打开一个(此时有很小的机会正在监听其他东西端口。)
  • 同意,但这取决于具体的用例:在我的情况下,我需要找到一个空闲端口号来将它交给一些 API(比如嵌入式 Jetty 启动器,用于测试)——相应的 API想要一个套接字号 - 不是已经打开的服务器套接字。所以这取决于。
  • @vorburger 合理的 API 将接受零作为要侦听的有效端口号,然后会告诉您正在侦听的实际端口。然而,合理的 API 并不多:很多程序专门测试 0 端口是否通过并拒绝它(ssh -D 127.0.0.0:0 ...?不!),这真的很令人沮丧。我们必须修补相当多的库/程序才能使它们对我们有用。
  • @vorburger 使用 any 端口时,应小心关闭它。 new ServerSocket(0) 在这方面没有什么不同。您的第一个链接中没有任何关于此的内容。您的第二个链接仅表现出糟糕的做法。一旦您构建了ServerSocket,您应该使用它,而不是关闭它并尝试重用相同的端口号。所有这些都是完全浪费时间,而且容易受到时间窗口问题的影响。
【解决方案2】:

如果你将0作为端口号传递给ServerSocket的构造函数,它会为你分配一个端口。

【讨论】:

  • 是的,我认为这是最优雅的解决方案,但由于某些原因,它不适用于我的情况。但我仍然需要找出到底是哪里出了问题。
  • @Roman:为什么它不起作用?更新您的问题以包含此内容(否则人们会继续提出建议)并解释为什么此解决方案对您失败。
  • 如何从客户端找到这个端口? ;)
【解决方案3】:

从 Java 1.7 开始,您可以像这样使用 try-with-resources:

  private Integer findRandomOpenPortOnAllLocalInterfaces() throws IOException {
    try (
        ServerSocket socket = new ServerSocket(0);
    ) {
      return socket.getLocalPort();

    }
  }

如果您需要在特定接口上查找开放端口,请查看 ServerSocket 文档以获取替代构造函数。

警告: 任何使用此方法返回的端口号的代码都会受到竞争条件的影响——在我们关闭 ServerSocket 实例后,不同的进程/线程可能会立即绑定到同一个端口。

【讨论】:

  • 如果您不设置SO_REUSEADDR,这可能不起作用。还有一个竞争条件,但很难解决。
  • 如果您随后尝试使用相同的端口号打开 另一个 ServerSocket,这可能不起作用,因为它可能同时被占用。为什么你不只返回实际的ServerSocket 而不是关闭它仍然是个谜。
  • @EJP 有时第三方代码接受端口但不接受套接字本身。
  • @CaptainMan 当然可以,但在这些情况下,肯定会假定端口是预先分配的。动态分配的端口必须通过一些其他机制提供给客户端,例如命名服务或广播或多播。这里的整个问题格式不正确。
  • @user207421 您不能更改第三方代码以接受ServerSocket 而不是int 端口。
【解决方案4】:

根据Wikipedia,如果您不需要“众所周知的”端口,您应该使用端口4915265535

AFAIK 确定端口是否正在使用的唯一方法是尝试打开它。

【讨论】:

  • +1。由于 Wikipedia 并不总是绝对真理/事实的来源,我认为需要注意的是,Wikipedia 页面的参考来自“Internet Assigned Numbers Authority (IANA)”的“[Service Name and Transport Protocol Port Number”注册表(iana.org/assignments/service-names-port-numbers/…" 页面,基于RFC 6335 - Section 6(即本例中的“Solid”引用!:))。
【解决方案5】:

如果您需要在范围使用:

public int nextFreePort(int from, int to) {
    int port = randPort(from, to);
    while (true) {
        if (isLocalPortFree(port)) {
            return port;
        } else {
            port = ThreadLocalRandom.current().nextInt(from, to);
        }
    }
}

private boolean isLocalPortFree(int port) {
    try {
        new ServerSocket(port).close();
        return true;
    } catch (IOException e) {
        return false;
    }
}

【讨论】:

  • 想知道如何检查端口,然后解除绑定。谢谢谢尔盖!
  • 如果 [from,to) 范围内的每个端口都在使用,则此代码将无限循环(或至少直到其中一个端口空闲)。如果您进行顺序扫描而不是随机选择范围内的端口,则可以避免这种可能性(当您到达范围末尾而没有找到空闲端口时抛出异常)。如果你真的需要随机选择端口,那么你需要一个集合来跟踪你迄今为止尝试过的端口,然后在该集合的大小等于 - 时引发错误。
  • Port 0 相当简单,在成功的情况下不需要创建两个ServerSockets,并且不会受到此代码的时间窗口问题的影响。
【解决方案6】:

【讨论】:

  • 对于那些好奇的人,SpringUtils.findAvailableTcpPort() 在幕后所做的与其他答案中推荐的完全相同:选择一个端口,然后尝试new ServerSocket(port)
【解决方案7】:

Eclipse SDK 包含一个类SocketUtil,它可以满足您的需求。你可以看看 git source code

【讨论】:

  • 看 Eclipse 的代码,他们做的和 Graham Edgecombe 的回答一样
【解决方案8】:

这适用于我在 Java 6 上

    ServerSocket serverSocket = new ServerSocket(0);
    System.out.println("listening on port " + serverSocket.getLocalPort());

【讨论】:

    【解决方案9】:

    我最近发布了一个小型库,用于执行此操作并考虑到测试。 Maven依赖是:

    <dependency>
        <groupId>me.alexpanov</groupId>
        <artifactId>free-port-finder</artifactId>
        <version>1.0</version>
    </dependency>
    

    安装后,可以通过以下方式获取空闲端口号:

    int port = FreePortFinder.findFreeLocalPort();
    

    【讨论】:

      【解决方案10】:

      ServerSocket:

      创建一个绑定到指定端口的服务器套接字。端口 0 在任何空闲端口上创建一个套接字。

      【讨论】:

        【解决方案11】:

        如果你的服务器启动了,那么那个套接字就没有被使用。

        编辑

        类似:

        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(或任何它),你再试一次,直到它成功获得一个端口。
        • 我认为最好检查一个端口是否可用,然后尝试开始监听这个端口。开始做某事,然后发现有问题,然后尝试解决这些问题,似乎并不优雅。
        • @Roman 好吧,是的,那会更好,除了没有方法可以知道端口是否可用。如果new ServerSocket(0) 不适合您的替代方案是这个。我认为你有 85% 的可能性最终会使用我的建议。
        • 这段代码一直在打开和泄漏ServerSockets,并且只保留成功的最后一个。它需要一个break 声明。
        【解决方案12】:

        如果您想使用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();
        

        【讨论】:

          【解决方案13】:

          如果您使用的是 Spring,Michail Nikolaev 提供的答案是最简单和最干净的答案,应该支持 IMO。 为方便起见,我将使用 Springframwework SocketUtils.findAvailableTcpPort() 方法添加一个内联示例:

          int randomAvailablePort = SocketUtils.findAvailableTcpPort();

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

          【讨论】:

            【解决方案14】:

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

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

            定义的端口略多于 500 个,大约一半是 UDP,一半是 TCP。

            这些文件是使用 IANA 提供的信息制作的,请参阅 IANA Assigned port numbers

            【讨论】:

            • nmap 中有一个类似的(并且更完整的 IIRC)列表。 +1
            【解决方案15】:

            如果您使用的是 Spring 框架,最直接的方法是:

            private Integer laancNotifyPort = SocketUtils.findAvailableTcpPort();
            

            你也可以设置一个可接受的范围,它会在这个范围内搜索:

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

            这是一种方便的方法,可以抽象出复杂性,但在内部类似于此线程上的许多其他答案。

            【讨论】:

              【解决方案16】:

              使用“ServerSocket”类,我们可以识别给定端口是正在使用还是空闲。 ServerSocket 提供了一个构造函数,它接受一个整数(即端口号)作为 参数并在端口上初始化服务器套接字。如果 ServerSocket 抛出任何 IO 异常,那么我们可以假设这个端口已经在使用中。

              下面的sn -p用来获取所有可用的端口。

              for (int port = 1; port < 65535; port++) {
                       try {
                                ServerSocket socket = new ServerSocket(port);
                                socket.close();
                                availablePorts.add(port);
                       } catch (IOException e) {
              
                       }
              }
              

              参考link

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-03-26
                • 1970-01-01
                • 2012-05-04
                • 1970-01-01
                • 2014-04-24
                • 2020-08-16
                相关资源
                最近更新 更多