Sockets: Ermitteln Sie die Verfügbarkeit von Ports mithilfe von Java


123

Wie bestimme ich programmgesteuert die Verfügbarkeit eines Ports auf einem bestimmten Computer mithilfe von Java?

dh wenn Sie eine Portnummer angegeben haben, stellen Sie fest, ob diese bereits verwendet wird oder nicht.


2
Während es bei dieser Frage darum geht, wie Sie feststellen können, ob ein bestimmter Port bereits verwendet wird, sind Sie möglicherweise hier gelandet, um einen Weg zu finden, eine kostenlose Portnummer zu erhalten, die stackoverflow.com/questions/2675362/… (mit Link zu meinem Kern) enthält .github.com / 3429822 ) deckt besser ab.
Vorburger

Für welchen Zweck? Wenn Sie nur einen freien Socket zum Abhören suchen möchten, geben Sie einfach Null an. Jede Scan-Technik unterliegt Zeitfensterproblemen: Der Port kann beim Scannen frei und belegt sein, wenn Sie einen Anspruch geltend machen.
Marquis of Lorne

Antworten:


91

Dies ist die Implementierung aus dem Apache- Kamelprojekt :

/**
 * 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;
}

Sie überprüfen auch das DatagramSocket, um zu überprüfen, ob der Port in UDP und TCP verfügbar ist.

Hoffe das hilft.


5
Eine nette Variante davon, wenn Sie MINA haben, ist AvailablePortFinder.getNextAvailable (1024), mit dem Sie den nächsten nicht privilegierten Port erhalten.
Alain O'Dea

9
Dies fängt jedoch nicht alles. Ich wurde von einem laufenden Nginx auf TCP 8000 gebissen, während die obige Routine 8000 als verfügbar meldet. Keine Ahnung warum - ich vermute, Nginx macht ein paar hinterhältige Sachen (dies ist unter OS X). Problemumgehung für mich ist, das oben Genannte zu tun und auch einen neuen Socket ("localhost", Port) zu öffnen und dann false zurückzugeben, wenn wir keine Ausnahme erhalten. Gedanken?
Teilweise bewölkt

2
Das gleiche Problem unter Windows, das versucht zu erkennen, ob der SMTP-Server mit Papierschnitt ausgeführt wird. Eine Lösung, bei der Sie eine Verbindung zu einem Port herstellen, anstatt zu versuchen, sich an einen Port zu binden (wie in Partly Clodys Kommentar und Iwans Antwort vorgeschlagen), scheint zuverlässiger zu funktionieren.
Stian

4
Interessant. Der Aufruf von setReuseAddress () ist völlig sinnlos, nachdem der Socket bereits gebunden ist, und widerspricht auch völlig dem Zweck des Codes.
Marquis von Lorne

3
Dieser Code unterliegt Zeitfensterproblemen. Wenn das Ziel darin besteht, einen ServerSocketabhörenden Port zu erstellen , sollte dies der Fall sein und der zurückgegeben werden ServerSocket. Das Erstellen und anschließende Schließen und Zurückgeben bietet keine Garantie dafür, dass ServerSocketüber diesen Port eine nachfolgende erstellt werden kann. Das Gleiche gilt für DatagramSocket.
Marquis von Lorne

41

Für Java 7 können Sie try-with-resource für kompakteren Code verwenden:

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

15
Dies testet nicht, ob am Port verfügbar ist. Es testet, ob es sich im LISTEN-Status befindet, ob die IP-Adresse erreichbar ist usw.
Marquis of Lorne

1
Außerdem ist dieser Test ziemlich langsam (fast eine Sekunde pro Port).
JMax

Sollte es nicht false zurückgeben, wenn eine IOException abgefangen wird?
Baroudi Safwen

@ BaroudiSafwen Es hängt ganz davon ab, was die Ausnahme tatsächlich ist. Für ConnectException: 'connection refused', ja sollte es false zurück. Für Zeitüberschreitungen ist nichts gültig, was zurückgegeben werden könnte, da die tatsächliche Antwort nicht bekannt ist. Deshalb ist diese Technik für diesen Zweck nutzlos.
Marquis von Lorne

36

Ab Java 7 scheint die Antwort von David Santamaria nicht mehr zuverlässig zu funktionieren. Es sieht jedoch so aus, als könnten Sie die Verbindung weiterhin zuverlässig mit einem Socket testen.

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

Ich möchte überprüfen, ob ein Proxyserver verfügbar ist, um in seinem Namen einen Anruf zu tätigen.
Dejell

1
@ DavidSantamarias Antwort hat nie funktioniert. Nichts mit Java 7 zu tun.
Marquis von Lorne

35

Wenn Sie sich nicht zu sehr mit der Leistung befassen, können Sie jederzeit versuchen, einen Port mit der ServerSocket- Klasse abzuhören . Wenn es eine Ausnahme auslöst, werden die Chancen verwendet.

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

BEARBEITEN: Wenn Sie nur versuchen, einen freien Port auszuwählen new ServerSocket(0), finden Sie einen für Sie.


Verwenden Sie finally für die Bereinigung (versuchen Sie {...} catch {...} finally {if (socket! = Null) socket.close ();}
Hosam Aly

Sie können auch "portTaken = (socket == null);" am Ende anstatt es im Fang zu tun.
Hosam Aly

1
Ich hatte nicht das Gefühl, dass dies in den beabsichtigten Rahmen der Frage des Autors fällt.
Spencer Ruport

1
Gibt es eine Möglichkeit, dies ohne Versuch / Fang zu tun? Es würde mir nichts ausmachen, einen verfügbaren Port für meine Zwecke zu verwenden, aber es ist ein wenig verschwenderisch, über Portnummern zu iterieren und zu versuchen, jeden zu durchsuchen, bis ich einen freien finde. Gibt es nicht irgendwo eine 'native boolean available (int)' Methode, die nur prüft?
Oren Shalev

27
Das neue SeverSocket (0) wählt automatisch einen freien Port aus.
Spencer Ruport

10

Die folgende Lösung ist von der SocketUtils- Implementierung von Spring-Core (Apache-Lizenz) inspiriert .

Im Vergleich zu anderen Lösungen ist die Verwendung Socket(...)ziemlich schnell (Testen von 1000 TCP-Ports in weniger als einer Sekunde):

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

Die auf Try / Catch-Sockets basierenden Lösungen liefern möglicherweise keine genauen Ergebnisse (die Socket-Adresse lautet "localhost", und in einigen Fällen kann der Port nicht von der Loopback-Schnittstelle "belegt" werden, und zumindest unter Windows ist dieser Test fehlgeschlagen, z der Schutz fälschlicherweise als verfügbar deklariert).

Es gibt eine coole Bibliothek namens SIGAR , der folgende Code kann Sie verbinden:

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;

Ich erhalte: "no sigar-x86-winnt.dll in java.library.path" Leider muss eine zusätzliche DLL heruntergeladen werden, die in einer Unternehmensumgebung nicht wirklich realisierbar ist (ich habe keine Kontrolle über das CI-System). So schlimm ... Trotzdem danke.
Uthomas

@uthomas Ich denke, Sie haben immer noch die Kontrolle über Ihr App-Bereitstellungspaket. Wenn dies der Fall ist, können Sie immer native Sigar-Laufzeit-DLLs / SOs in der Nähe Ihrer JARs bereitstellen und den JAVA-Lib-Pfad pragmatisch festlegen (über einen einfachen Hack können Sie ihn auch nach Ihrem ändern App wurde von der JVM gestartet).
Shmil The Cat

2

Eine Bereinigung der Antwort von 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;
    }
}

Dies unterliegt immer noch einer Rennbedingung, auf die user207421 in den Kommentaren zu David Santamarias Antwort hingewiesen hat (etwas könnte den Port greifen, nachdem diese Methode das ServerSocketund geschlossen hat DatagramSocketund zurückkehrt).


1

In meinem Fall hat es geholfen, eine Verbindung zum Port herzustellen. Wenn der Dienst bereits vorhanden ist, reagiert er.

    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
Nicht das, wonach gefragt wurde.
Marquis von Lorne

0

In meinem Fall musste ich die DatagramSocket-Klasse verwenden.

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

Vergessen Sie nicht, zuerst zu importieren

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

Dies funktioniert für UDP-Ports. Die Frage scheint sich auf TCP-Ports zu beziehen.
Marquis von Lorne

-2

Ich habe so etwas ausprobiert und es hat bei mir sehr gut funktioniert

            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
Nicht das, wonach gefragt wurde.
Marquis von Lorne

Und auch ein Steckdosenleck.
Marquis von Lorne
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.