Sockets: 使用 Java 发现端口可用性

如何使用 Java 以编程方式确定给定机器中端口的可用性?

即给定一个端口号,确定它是否已经被使用。

168186 次浏览

如果不太关心性能,可以尝试使用 ServerSocket类侦听端口。如果它抛出一个异常,它就有可能被使用。

public static boolean isAvailable(int portNr) {
boolean portFree;
try (var ignored = new ServerSocket(portNr)) {
portFree = true;
} catch (IOException e) {
portFree = false;
}
return portFree;
}

编辑: 如果你只是想选择一个自由端口,那么 new ServerSocket(0)会为你找到一个。

这是来自 Apache 骆驼项目的 实施:

/**
* Checks to see if a specific port is available.
*
* @param port the port to check for availability
*/
public static boolean available(int port) {
if (port < MIN_PORT_NUMBER || port > MAX_PORT_NUMBER) {
throw new IllegalArgumentException("Invalid start port: " + port);
}


ServerSocket ss = null;
DatagramSocket ds = null;
try {
ss = new ServerSocket(port);
ss.setReuseAddress(true);
ds = new DatagramSocket(port);
ds.setReuseAddress(true);
return true;
} catch (IOException e) {
} finally {
if (ds != null) {
ds.close();
}


if (ss != null) {
try {
ss.close();
} catch (IOException e) {
/* should not be thrown */
}
}
}


return false;
}

他们也在检查 DatagramSocket,以检查端口是否在 UDP 和 TCP 中可用。

希望这个能帮上忙。

在我的案例中,它帮助尝试连接到端口-如果服务已经存在,它将响应。

    try {
log.debug("{}: Checking if port open by trying to connect as a client", portNumber);
Socket sock = new Socket("localhost", portNumber);
sock.close();
log.debug("{}: Someone responding on port - seems not open", portNumber);
return false;
} catch (Exception e) {
if (e.getMessage().contains("refused")) {
return true;
}
log.error("Troubles checking if port is open", e);
throw new RuntimeException(e);
}

从 Java7开始,David Santamaria 的回答似乎不再可靠地工作了。但是,您似乎仍然可以可靠地使用 Socket 来测试连接。

private static boolean available(int port) {
System.out.println("--------------Testing port " + port);
Socket s = null;
try {
s = new Socket("localhost", port);


// If the code makes it this far without an exception it means
// something is using the port and has responded.
System.out.println("--------------Port " + port + " is not available");
return false;
} catch (IOException e) {
System.out.println("--------------Port " + port + " is available");
return true;
} finally {
if( s != null){
try {
s.close();
} catch (IOException e) {
throw new RuntimeException("You should handle this error." , e);
}
}
}
}

对于 Java7,您可以使用 try-with-resource 来编写更紧凑的代码:

private static boolean available(int port) throws IllegalStateException {
try (Socket ignored = new Socket("localhost", port)) {
return false;
} catch (ConnectException e) {
return true;
} catch (IOException e) {
throw new IllegalStateException("Error while trying to check open port", e);
}
}

基于 try/catch 套接字的解决方案可能不会产生准确的结果(套接字地址是“ localhost”,在某些情况下端口可能被“占用”而不是环回接口,至少在 Windows 上我看到这个测试失败了,例如错误声明为可用的保护)。

有一个很酷的库名为 SIGAR,下面的代码可以帮助你:

Sigar sigar = new Sigar();
int flags = NetFlags.CONN_TCP | NetFlags.CONN_SERVER | NetFlags.CONN_CLIENT;             NetConnection[] netConnectionList = sigar.getNetConnectionList(flags);
for (NetConnection netConnection : netConnectionList) {
if ( netConnection.getLocalPort() == port )
return false;
}
return true;

我试过这样的方法,效果很好

            Socket Skt;
String host = "localhost";
int i = 8983; // port no.


try {
System.out.println("Looking for "+ i);
Skt = new Socket(host, i);
System.out.println("There is a Server on port "
+ i + " of " + host);
}
catch (UnknownHostException e) {
System.out.println("Exception occured"+ e);


}
catch (IOException e) {
System.out.println("port is not used");


}

在我的例子中,我必须使用 DatagramSocket 类。

boolean isPortOccupied(int port) {
DatagramSocket sock = null;
try {
sock = new DatagramSocket(port);
sock.close();
return false;
} catch (BindException ignored) {
return true;
} catch (SocketException ex) {
System.out.println(ex);
return true;
}
}

别忘了先进口

import java.net.DatagramSocket;
import java.net.BindException;
import java.net.SocketException;

下面的解决方案受到 Spring-core (Apache 许可证)的 SocketUtils实现的启发。

与使用 Socket(...)的其他解决方案相比,它非常快(在不到一秒钟的时间内测试1000个 TCP 端口)。

当然,它只能检测在所有接口上打开端口或在本地主机上显式运行的服务。

public static boolean isTcpPortAvailable(int port) {
try (ServerSocket serverSocket = new ServerSocket()) {
// setReuseAddress(false) is required only on macOS,
// otherwise the code will not work correctly on that platform
serverSocket.setReuseAddress(false);
serverSocket.bind(new InetSocketAddress(InetAddress.getByName("localhost"), port), 1);
return true;
} catch (Exception ex) {
return false;
}
}

大卫•桑塔马利亚(David Santamaria)对答案进行了梳理:

/**
* Check to see if a port is available.
*
* @param port
*            the port to check for availability.
*/
public static boolean isPortAvailable(int port) {
try (var ss = new ServerSocket(port); var ds = new DatagramSocket(port)) {
return true;
} catch (IOException e) {
return false;
}
}

这仍然受到 user207421在对 David Santamaria 的回答的评论中指出的竞争条件的影响(在这个方法关闭 ServerSocketDatagramSocket并返回之后,可能会有东西抓住端口)。