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应用程序嵌入式服务器。 ServerConnector
类setPort
方法显式设置它,否则它将自动为我们分配一个空闲端口:
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嵌入式服务器,其工作方式略有不同。 Tomcat
类setPort
方法指定一个明确的端口号。如果我们提供的端口号为零,则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
类。
0 评论