在Java中找到可用端口

1.概述

在Java應用程序中啟動套接字服務器時, java.net API要求我們指定一個空閒端口號以進行偵聽。端口號是必需的,以便TCP層可以識別輸入數據打算用於的應用程序。

明確指定端口號並不總是一個好的選擇,因為應用程序可能已經佔用了它。這將在我們的Java應用程序中導致輸入/輸出異常。

在本快速教程中,我們將研究如何檢查特定的端口狀態以及如何使用自動分配的端口狀態。我們將研究如何使用普通的Java和Spring框架來完成此任務。我們還將研究其他一些服務器實現,例如嵌入式Tomcat和Jetty。

2.檢查端口狀態

java.net API檢查特定端口是空閒還是佔用。

2.1 特殊端口

我們將使用java.net ServerSocket類來創建綁定到指定端口的服務器套接字。 ServerSocket**在其構造函數中**接受一個顯式端口號。該類還實現了Closeable接口,因此可以在try-with-resources使用它來自動關閉套接字並釋放端口:

try (ServerSocket serverSocket = new ServerSocket(FREE_PORT_NUMBER)) {

 assertThat(serverSocket).isNotNull();

 assertThat(serverSocket.getLocalPort()).isEqualTo(FREE_PORT_NUMBER);

 } catch (IOException e) {

 fail("Port is not available");

 }

如果我們使用了兩次特定端口,或者該端口已經被另一個應用程序佔用,則ServerSocket構造函數將拋出IOException

try (ServerSocket serverSocket = new ServerSocket(FREE_PORT_NUMBER)) {

 new ServerSocket(FREE_PORT_NUMBER);

 fail("Same port cannot be used twice");

 } catch (IOException e) {

 assertThat(e).hasMessageContaining("Address already in use");

 }

2.2 端口範圍

現在,讓我們檢查如何利用拋出的IOException,使用給定端口號範圍內的第一個空閒端口創建服務器套接字:

for (int port : FREE_PORT_RANGE) {

 try (ServerSocket serverSocket = new ServerSocket(port)) {

 assertThat(serverSocket).isNotNull();

 assertThat(serverSocket.getLocalPort()).isEqualTo(port);

 return;

 } catch (IOException e) {

 assertThat(e).hasMessageContaining("Address already in use");

 }

 }

 fail("No free port in the range found");

3.查找空閒端口

使用顯式端口號並不總是一個好的選擇,因此讓我們研究一下自動分配空閒端口的可能性。

3.1 純Java

ServerSocket類構造函數中使用特殊的端口號零。結果, java.net API將自動為我們分配一個空閒端口:

try (ServerSocket serverSocket = new ServerSocket(0)) {

 assertThat(serverSocket).isNotNull();

 assertThat(serverSocket.getLocalPort()).isGreaterThan(0);

 } catch (IOException e) {

 fail("Port is not available");

 }

3.2 Spring框架

Spring框架包含一個SocketUtils類,我們可以使用它來找到可用的空閒端口。它的內部實現使用ServerSocket類,如前面的示例所示:

int port = SocketUtils.findAvailableTcpPort();

 try (ServerSocket serverSocket = new ServerSocket(port)) {

 assertThat(serverSocket).isNotNull();

 assertThat(serverSocket.getLocalPort()).isEqualTo(port);

 } catch (IOException e) {

 fail("Port is not available");

 }

4.其他服務器實施

現在讓我們看一下其他一些流行的服務器實現。

4.1 Jetty

Jetty是一種非常流行的Java應用程序嵌入式服務器。 ServerConnectorsetPort方法顯式設置它,否則它將自動為我們分配一個空閒端口:

Server jettyServer = new Server();

 ServerConnector serverConnector = new ServerConnector(jettyServer);

 jettyServer.addConnector(serverConnector);

 try {

 jettyServer.start();

 assertThat(serverConnector.getLocalPort()).isGreaterThan(0);

 } catch (Exception e) {

 fail("Failed to start Jetty server");

 } finally {

 jettyServer.stop();

 jettyServer.destroy();

 }

4.2 Tomcat

Tomcat是另一種流行的Java嵌入式服務器,其工作方式略有不同。 TomcatsetPort方法指定一個明確的端口號。如果我們提供的端口號為零,則Tomcat將自動分配一個空閒端口。但是,如果我們未設置任何端口號,則Tomcat將使用其默認端口8080。請注意,默認Tomcat端口可能被其他應用程序佔用:

Tomcat tomcatServer = new Tomcat();

 tomcatServer.setPort(0);

 try {

 tomcatServer.start();

 assertThat(tomcatServer.getConnector().getLocalPort()).isGreaterThan(0);

 } catch (LifecycleException e) {

 fail("Failed to start Tomcat server");

 } finally {

 tomcatServer.stop();

 tomcatServer.destroy();

 }

5.結論

在本文中,我們探討瞭如何檢查特定的端口狀態。我們還介紹了從一系列端口號中查找空閒端口的過程,並說明瞭如何使用自動分配的空閒端口。

在示例中,我們介紹了java.net API和其他流行的服務器實現(包括Jetty和Tomcat)中ServerSocket類。