14
\$\begingroup\$

Review this code, which should return true if a port is in use or false if the port is not in use.

Clarification: "In use" means that the port is already open (and used by another application). I'm trying to find a port number between (49152 and 65535) to open that is available.

private boolean isPortInUse(String hostName, int portNumber) {
        boolean result;

        try {

            Socket s = new Socket(hostName, portNumber);
            s.close();
            result = true;

        }
        catch(Exception e) {
            result = false;
        }

        return(result);
}
\$\endgroup\$
1
  • 2
    \$\begingroup\$ Catching Exception is not a good practice try to catch the known subclass which you expect... \$\endgroup\$ Sep 20, 2013 at 11:06

4 Answers 4

8
\$\begingroup\$

Don't test for port-in-use in advance. Just let your application do what it wants to do, and catch the exception there. The situation can change between the time you test and when you actually try to use the port.

Furthermore, if you are trying to write a server application, you can have Java automatically pick a free port for you. From the JavaDoc for ServerSocket(int):

Creates a server socket, bound to the specified port. A port number of 0 means that the port number is automatically allocated, typically from an ephemeral port range. This port number can then be retrieved by calling getLocalPort.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ not in all cases! When I am starting a long running test suite that relies on a port 30 minutes in, I want to know if that port is in use as early as possible. \$\endgroup\$
    – bnieland
    Jul 24, 2018 at 21:20
6
\$\begingroup\$

The code can be simplified somewhat:

private boolean isPortInUse(String host, int port) {
  // Assume no connection is possible.
  boolean result = false;

  try {
    (new Socket(host, port)).close();
    result = true;
  }
  catch(SocketException e) {
    // Could not connect.
  }

  return result;
}

Note, however, that if you want to check for an available port, that could be reflected in the method name:

private boolean availablePort(String host, int port) {
  // Assume port is available.
  boolean result = true;

  try {
    (new Socket(host, port)).close();

    // Successful connection means the port is taken.
    result = false;
  }
  catch(SocketException e) {
    // Could not connect.
  }

  return result;
}

Neither approaches employ object-oriented programming (OOP) techniques, though. In OOP, behaviour (determining port availability) is kept with the object that contains the attributes. For example:

public SocketAnalyzer extends java.net.Socket {
  public boolean isPortAvailable() {
    // ... code here ...
  }
}

Now the initial method simplifies to:

private boolean availablePort(String host, int port) {
  return (new SocketAnalyzer(host, port)).isPortAvailable();
}

And that allows the "availablePort" method to be removed completely, if desired:

if( (new SocketAnalyzer(host, port)).isPortAvailable() ) {
  // Launch the server socket on 'port'!
}

This promotes re-use, and shows one more idea: you could extend ServerSocket instead! The code would become:

// The constructor would have to bind to the host/port combination...
// This is arguably poor form as the constructor really shouldn't do anything.
// You could, instead, use the superclass' constructor and then call bind,
// but for the purposes of this example, the idea is key: inherit.
ServerSocketAnalyzer ssa = new ServerSocketAnalyzer( host, port );

if( ssa.isPortAvailable() ) {
  // Code to use the server socket...
  Socket s = ssa.accept();
}

Clean and simple, but there's a race condition. In between the time it takes to determine whether the port is available and the server starts accepting connections to that port, another service may have started to commandeer the port.

The best solution is to allow the server to pick an available port using an atomic operation, as per the other answer.

\$\endgroup\$
3
  • 2
    \$\begingroup\$ This invites the race condition (where you check that a port is available, but at the moment you try to bind to it it is no longer) \$\endgroup\$
    – sehe
    Jun 4, 2015 at 15:08
  • \$\begingroup\$ "Neither approaches employ object-oriented programming (OOP) techniques, though." OOP is not a goal in and of itself. \$\endgroup\$ May 2, 2019 at 20:20
  • \$\begingroup\$ OOP isn't a goal. Maintainable, robust (i.e., not brittle), and reliable software are goals. OOP methodologies are but one way to help achieve those goals. See also: stackoverflow.com/a/4002376/59087 and r.je/static-methods-bad-practice \$\endgroup\$ May 2, 2019 at 22:41
2
\$\begingroup\$

Your code takes a host parameter, but your test can only be true if you try to open a local port.

Your host parameter should be replaced by a hardcoded 127.0.0.1 or InetAddress.getLocalHost().getHostName()

If you try this on a remote machine, it will return true if the port is open and in use on the remote machine.

Consider:

private boolean isLocalPortInUse(int port) {
    try {
        // ServerSocket try to open a LOCAL port
        new ServerSocket(port).close();
        // local port can be opened, it's available
        return false;
    } catch(IOException e) {
        // local port cannot be opened, it's in use
        return true;
    }
}

Or :

private boolean isRemotePortInUse(String hostName, int portNumber) {
    try {
        // Socket try to open a REMOTE port
        new Socket(hostName, portNumber).close();
        // remote port can be opened, this is a listening port on remote machine
        // this port is in use on the remote machine !
        return true;
    } catch(Exception e) {
        // remote port is closed, nothing is running on
        return false;
    }
}
\$\endgroup\$
2
\$\begingroup\$

You can shorten this:

public static boolean isAvailable(String host, int portNr) {

    boolean isAvailable;

    try (var ignored = new Socket(host, portNr)) {
        // Successful connection means the port is taken
        isAvailable = false;
    } catch (IOException e) {
        // Could not connect
        isAvailable = true;
    }

    return isAvailable;
}

Keep in mind that on Linux, all ports up to 1024 can only be bound by root, meaning that isAvailable does not tell you whether you can actually bind your server to the returned port.

Here's a similar way using ServerSocket that has worked for my server application to find out whether I'm able to bind to a socket:

public static boolean canBind(String host, int portNr) {
    boolean canBind;
    var address InetAddress.getByName(host);

    try (var ignored = new ServerSocket(portNr, 0, address)) {
        canBind = true;
    } catch (IOException e) {
        canBind = false;
    }
    return canBind;
}

This is how you might use canBind to find a bindable port for your server (all this assumes a Java version of 10 or later):

public static void main(String... args) {

    var host = "0.0.0.0";

    var portRange = closedRange(8080, 9020);
    var portMaybe = portRange.stream()
            .filter(portNr -> canBind(host, portNr))
            .findFirst();

    portMaybe.ifPresentOrElse(port -> {
        var address = new InetSocketAddress(host, port);
        startServer(address);

    }, () -> System.err.println("Could not find port to bind to in this range: "
            + portRange));
}

private static List<Integer> closedRange(int startInclusive, int endInclusive) {
    return IntStream.rangeClosed(startInclusive, endInclusive)
            .boxed()
            .collect(Collectors.toList());
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.