如何找到可用端口?


194

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

  1. 我应该在哪个端口号范围内搜索?(我使用端口12345、12346和12347,这很好)。

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

Answers:


276

如果您不介意使用的端口,请为ServerSocket构造函数指定端口0,它将侦听任何可用端口。

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");
}

2
使用新的ServerSocket(0)时,请务必将其关闭!基于javasourcecode.org/html/open-source/eclipse/eclipse-3.5.2/org/...,稍微适应在我gist.github.com/3429822
vorburger

9
@vorburger,以这种方式进行操作很容易出现比赛条件。最好是立即在服务器套接字上侦听,而不是打开它以找到一个空闲端口,然后再次关闭它,然后在空闲端口上再次打开它(此时,现在很少有其他东西正在监听该端口)端口。)
Graham Edgecombe 2012年

7
同意,但是取决于确切的用例:在我的情况下,我需要找到一个免费的端口号以将其传递给某些API(例如,用于测试的嵌入式Jetty入门程序)-相应的API需要一个套接字号-尚未使用打开服务器套接字。因此,这取决于。
vorburger 2012年

4
@vorburger合理的API会接受零作为要侦听的有效端口号,然后会告诉您正在侦听的实际端口。但是,合理的API并不多:许多程序专门测试是否通过了0端口并拒绝它(ssh -D 127.0.0.0:0 ...?否!),这确实令人沮丧。我们必须修补许多库/程序,以使它们对我们有用。
Joker_vD 2014年

2
@vorburger使用任何端口时,请务必将其关闭。new ServerSocket(0)在这方面没有什么不同。您的第一个链接中没有关于此的任何内容。您的第二个链接仅表现为不良实践。构建完之后ServerSocket,就应该使用它,而不要关闭它并尝试重用相同的端口号。所有这些都完全浪费时间,并且容易受到时序窗口问题的影响。
洛恩侯爵,

57

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


是的,我认为这是最优雅的解决方案,但是由于某些原因,它不适用于我的情况。但是我仍然需要找出到底是什么错误。
罗马2010年

1
@罗马:为什么不起作用?更新您的问题以包括此问题(否则人们会不断提出建议),并说明为什么该解决方案对您而言无效。
FrustratedWithFormsDesigner

2
如何从客户端找到该端口?;)
ed22

34

从Java 1.7开始,您可以使用如下try-with-resources:

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

    }
  }

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

警告:使用此方法返回的端口号的任何代码都必须遵守竞争条件-关闭ServerSocket实例后,不同的进程/线程可能会立即绑定到同一端口。


1
如果您没有设置,这可能不起作用SO_REUSEADDR。虽然存在比赛条件,但很难解决。
David Ehrmann

如果您随后尝试使用相同的端口号打开另一个 ServerSocket端口,则此方法可能不起作用,因为这可能是在此同时进行的。为什么您不只是返回实际值ServerSocket而不是关闭它仍然是一个谜。
罗恩侯爵

5
@EJP有时第三方代码接受端口,但不接受套接字本身。
曼队长

@CaptainMan当然可以,但是在那种情况下,肯定会假定端口已被预先分配。动态分配的端口必须通过某种其他机制(例如,命名服务或广播或多播)提供给客户端。这里的整个问题格式不正确。
罗恩侯爵,

@ user207421您不能将第三方代码更改为接受端口的a ServerSocket代替int
上尉曼

30

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

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


6
+1。由于Wikipedia并不总是绝对的事实/事实的来源,因此,我认为有必要指出,该Wikipedia页面的参考来自“ Internet分配号码授权机构(IANA)”的“ [服务名称和传输协议端口号注册表(iana.org/assignments/service-names-port-numbers/… “页面,基于RFC 6335- 第6节(在这种情况下,即“ Solid”参考!:))。
OzgurH

25

如果您需要在范围内使用:

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;
    }
}

想知道如何检查端口,然后解除绑定。谢谢SergeyB!
弗拉德·伊利

5
如果[from,to]范围内的每个端口都在使用中,则此代码将无限循环(或至少直到这些端口中的一个变为空闲为止)。如果您进行顺序扫描,而不是随机选择范围内的端口,则可以避免这种情况(到达范围的末端而没有找到可用端口时,只抛出一个例外)。如果您确实需要随机选择端口,则需要一个集合来跟踪到目前为止您尝试过的端口,然后在该集合的大小等于-from时引发错误。
西蒙·基桑

端口0相当简单,ServerSockets在成功的情况下不需要创建两个端口,并且不会遭受此代码的计时窗口问题。
罗恩侯爵,




6

这对我适用于Java 6

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

6

我最近发布了一个小型库,它考虑了测试的目的。Maven依赖项是:

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

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

int port = FreePortFinder.findFreeLocalPort();

4

如果服务器启动,则不使用该套接字。

编辑

就像是:

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

我认为最好检查端口是否可用,然后尝试开始侦听此端口。开始某些事情然后发现存在问题然后尝试解决这些问题似乎并不明智。
罗马2010年

2
@Roman好吧,是的,这会更好,除了没有方法知道端口是否可用的事实。如果new ServerSocket(0)对您不起作用,替代方法是这样。我认为您最终有85%的可能性会使用我的建议。
OscarRyz

这段代码ServerSockets永远保持打开和泄漏的状态,只保留最后一个成功的代码。它需要一个break声明。
罗恩侯爵

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的一部分有一个类似的列表(更完整的是IIRC)。+1
rmeador

3

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

int randomAvailablePort = SocketUtils.findAvailableTcpPort();

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


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) {

         }
}

参考链接


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.