套接字:使用Java发现端口可用性


123

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

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


2
尽管此问题是关于如何知道给定端口是否已在使用中的,但您可能已经登陆此处,试图找到一种获取免费端口号的方法,该端口号stackoverflow.com/questions/2675362 / ...(包含指向我的主旨的链接).github.com / 3429822)涵盖了更好的内容。
vorburger

出于什么目的?如果您只想找到一个空的套接字来监听,只需指定零即可。任何扫描技术是容易时机窗口的问题:当你扫描端口可以是空的,并占领当你去索赔
洛恩侯爵

Answers:


91

这是实现从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中是否可用。

希望这可以帮助。


5
如果您拥有MINA,则可以使用的一个很好的变体是AvailablePortFinder.getNextAvailable(1024),它可以使您获得下一个非特权端口。
Alain O'Dea

9
但是,这并不能解决所有问题。我被在TCP 8000上运行nginx所咬住,而上述例程将8000报告为可用。不知道为什么-我怀疑nginx做了一些偷偷摸摸的事情(这在OS X上)。对我来说,解决方法是执行上述操作,同时打开一个新的Socket(“ localhost”,port),然后在没有异常的情况下返回false。有什么想法吗?
局部多云

2
Windows上试图检测Papercut SMTP服务器是否正在运行的相同问题。一种解决方案,其中您打开与端口的连接,而不是尝试绑定到端口(如Partly Clodys注释和Ivan的回答所建议),似乎更可靠。
stian 2012年

4
有趣。在套接字已经绑定之后,对setReuseAddress()的调用是完全没有意义的,并且这也完全违背了代码的目的。
罗恩侯爵

3
该代码受时序窗口问题的影响。如果目标是创建ServerSocket对某个端口的侦听,则应这样做,然后返回ServerSocket。创建它,然后关闭它并返回它提供了零保证,即ServerSocket可以使用该端口创建后续项。同上DatagramSocket
罗恩侯爵

41

对于Java 7,您可以使用try-with-resource获得更紧凑的代码:

private static boolean available(int port) {
    try (Socket ignored = new Socket("localhost", port)) {
        return false;
    } catch (IOException ignored) {
        return true;
    }
}

15
这不会测试at端口是否可用。它测试它是否处于LISTEN状态,IP地址是否可达,等等
洛恩侯爵

1
此外,此测试相当慢(每个端口将近一秒钟)。
JMax

捕获IOException时,它不应该返回false吗?
Baroudi Safwen

@BaroudiSafwen完全取决于异常实际是什么。对于ConnectException: 'connection refused',是,它应该返回false。对于超时,它无法返回的任何内容都是有效的,因为实际答案未知。这就是为什么这种技术对这个目的没有用的原因。
罗恩侯爵,

36

从Java 7开始,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);
            }
        }
    }
}

我想检查代理服务器是否可以代表他拨打电话。
Dejell

1
@DavidSantamaria的答案从未奏效。与Java 7无关
洛恩侯爵

35

如果您不太关心性能,则可以始终尝试使用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)它将为您找到一个。


最终用于清理(尝试{...}捕获{...}最后{如果(套接字!= null)socket.close();}
Hosam Aly

您还可以设置“ portTaken =(套接字== null);” 最后而不是在陷阱中做。
Hosam Aly

1
我不认为这属于作者提出的问题的预期范围。
斯宾塞·鲁波特

1
有没有没有尝试/捕获的方法?我不介意为我的目的使用任何可用的端口,但是遍历端口号并尝试/捕获每个端口,直到我发现一个空闲的端口有点浪费。在某个地方是否没有“ native boolean available(int)”方法,该方法只是检查?
Oren Shalev

27
新的SeverSocket(0)将自动选择一个空闲端口。
斯宾塞·鲁波特

10

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

与使用Socket(...)它的其他解决方案相比,速度非常快(不到一秒钟即可测试1000个TCP端口):

public static boolean isTcpPortAvailable(int port) {
    try (ServerSocket serverSocket = new ServerSocket()) {
        // setReuseAddress(false) is required only on OSX, 
        // 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;
    }
}       

3

基于try / catch套接字的解决方案可能无法产生准确的结果(套接字地址为“ localhost”,并且在某些情况下,端口可能会被“回送”接口“占用”,至少在Windows上,我已经看到该测试失败,即prot被错误地声明为可用)。

有一个很酷的库,名为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;

我得到:“ java.library.path中没有sigar-x86-winnt.dll”不幸的是,它需要下载一些在企业环境中实际上不可行的其他dll(我无法控制CI系统)。太糟糕了。
uthomas

@uthomas我想您仍然可以控制您的应用程序部署包,如果是这种情况,您始终可以在JAR附近提供Sigar本机运行时DLL / SO,并以实用的方式设置JAVA lib路径(通过简单的hack,即使您在应用程序已由JVM启动)。
Shmil The Cat 2013年

2

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在评论大卫·圣玛丽亚的回答中指出的竞争条件(东西可以抓住港口此方法关闭后ServerSocketDatagramSocket返回)。


1

就我而言,它有助于尝试并连接到端口-如果服务已经存在,它将响应。

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

1
不是要求的。
罗恩侯爵,2015年

0

就我而言,我必须使用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;

这适用于UDP端口。问题似乎与TCP端口有关。
洛恩侯爵

-2

我已经试过像这样的东西,对我来说真的很好

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

                 }

1
不是要求的。
罗恩侯爵,

而且还有套接字泄漏。
罗恩侯爵'18年
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.