Gniazda: Odkryj dostępność portów za pomocą języka Java

123

Jak programowo określić dostępność portu na danej maszynie przy użyciu języka Java?

tzn. mając numer portu, określ, czy jest już używany, czy nie ?.

user54075
źródło
2
Chociaż to pytanie dotyczy tego, jak sprawdzić, czy dany port jest już używany, być może wylądowałeś tutaj, próbując znaleźć sposób na uzyskanie wolnego numeru portu, który stackoverflow.com/questions/2675362/ ... (z linkiem do mojego sedna .github.com / 3429822 ) zakrywa lepiej.
vorburger
W jakim celu? Jeśli chcesz tylko znaleźć wolne gniazdo do nasłuchiwania, po prostu podaj zero. Każda technika skanowania może powodować problemy z oknem czasowym: port może być pusty podczas skanowania i zajęty, gdy idziesz zgłosić się do niego.
Marquis of Lorne

Odpowiedzi:

91

Oto implementacja pochodząca z projektu wielbłąda 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;
}

Sprawdzają również DatagramSocket, aby sprawdzić, czy port jest dostępny w UDP i TCP.

Mam nadzieję że to pomoże.

David Santamaria
źródło
5
Fajnym wariantem tego, jeśli masz MINA, jest AvailablePortFinder.getNextAvailable (1024), który pobiera następny nieuprzywilejowany port.
Alain O'Dea
9
To jednak nie wszystko. Zostałem ugryziony przez działający nginx na TCP 8000, podczas gdy powyższa procedura zgłasza 8000 jako dostępne. Nie mam pojęcia, dlaczego - podejrzewam, że nginx robi podstępne rzeczy (to jest na OS X). Obejściem dla mnie jest wykonanie powyższego, a także otwarcie nowego Socket („localhost”, port), a następnie zwrócenie false, jeśli nie otrzymamy wyjątku. Myśli?
Częściowe zachmurzenie
2
Ten sam problem w systemie Windows próbuje wykryć, czy serwer SMTP papercut jest uruchomiony. Rozwiązanie, w którym otwierasz połączenie z portem, zamiast próbować się z nim połączyć (jak sugeruje komentarz Partly Clodys i odpowiedź Ivana) wydaje się działać bardziej niezawodnie.
stian
4
Ciekawy. Wywołanie setReuseAddress () jest całkowicie bezcelowe po tym, jak gniazdo jest już związane, a także jest całkowicie sprzeczne z celem kodu.
Markiz Lorne
3
Ten kod podlega problemom z oknami czasowymi. Jeśli celem jest stworzenie ServerSocketpodsłuchiwania jakiegoś portu, to powinno to zrobić i zwrócić plik ServerSocket. Utworzenie go, a następnie zamknięcie i zwrócenie nie daje żadnej gwarancji, że kolejny ServerSocketmożna utworzyć przy użyciu tego portu. To samo dotyczy DatagramSocket.
Markiz Lorne
41

W przypadku Java 7 możesz użyć try-with-resource, aby uzyskać bardziej zwarty kod:

private static boolean available(int port) {
    try (Socket ignored = new Socket("localhost", port)) {
        return false;
    } catch (IOException ignored) {
        return true;
    }
}
eivindw
źródło
15
To nie sprawdza, czy port jest dostępny. Sprawdza, czy jest w stanie LISTEN, czy adres IP jest osiągalny itp.
Marquis of Lorne
1
Dodatkowo ten test jest dość powolny (prawie sekunda na port).
JMax
czy nie powinien zwracać false, gdy zostanie przechwycony IOException?
Baroudi Safwen
@BaroudiSafwen To zależy całkowicie od tego, jaki faktycznie jest wyjątek. Bo ConnectException: 'connection refused'tak, to powinno zwrócić fałsz. W przypadku przekroczenia limitów czasu nic, co mogłoby zwrócić, nie byłoby ważne, ponieważ rzeczywista odpowiedź nie jest znana. Dlatego ta technika jest bezużyteczna do tego celu.
Markiz Lorne
36

Wydaje się, że od wersji Java 7 odpowiedź Davida Santamaria nie działa już niezawodnie. Wygląda jednak na to, że nadal możesz niezawodnie używać Socket do testowania połączenia.

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);
            }
        }
    }
}
TwentyMiles
źródło
Chcę sprawdzić, czy dostępny jest serwer proxy, aby dzwonić w jego imieniu.
Dejell
1
Odpowiedź @ DavidSantamaria nigdy nie zadziałała. Nie ma nic wspólnego z Javą 7.
Markiz Lorne
35

Jeśli nie przejmujesz się zbytnio wydajnością, zawsze możesz spróbować nasłuchiwać na porcie przy użyciu klasy ServerSocket . Jeśli zgłosi wyjątek, szanse na to, że są używane.

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

EDYCJA: Jeśli wszystko, co próbujesz zrobić, to wybrać wolny port new ServerSocket(0), znajdziesz go dla siebie.

Spencer Ruport
źródło
Użyj w końcu do czyszczenia (spróbuj {...} catch {...} w końcu {if (gniazdo! = Null) socket.close ();}
Hosam Aly,
Możesz także ustawić "portTaken = (socket == null);" na końcu zamiast robić to w haczyku.
Hosam Aly,
1
Nie czułem, że mieści się to w zamierzonym zakresie pytania autora.
Spencer Ruport
1
Czy można to zrobić bez try / catch? Nie miałbym nic przeciwko używaniu dowolnego dostępnego portu do moich celów, ale iterowanie po numerach portów i próbowanie / przechwytywanie każdego z nich, dopóki nie znajdę wolnego, jest trochę marnotrawstwem. Czy nie ma gdzieś metody „natywnej dostępnej wartości logicznej (int)”, która po prostu sprawdza?
Oren Shalev
27
new SeverSocket (0) automatycznie wybierze wolny port.
Spencer Ruport
10

Poniższe rozwiązanie jest inspirowane implementacją Spring-core przez SocketUtils (licencja Apache).

W porównaniu do innych rozwiązań wykorzystujących Socket(...)go jest dość szybko (testowanie 1000 portów TCP w mniej niż sekundę):

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;
    }
}       
JMax
źródło
3

Rozwiązania oparte na gnieździe try / catch mogą nie dawać dokładnych wyników (adres gniazda to „localhost”, aw niektórych przypadkach port może być „zajęty” nie przez interfejs pętli zwrotnej, a przynajmniej w systemie Windows ten test nie powiódł się, tj. protokół fałszywie zadeklarowany jako dostępny).

Istnieje fajna biblioteka o nazwie SIGAR , poniższy kod może Cię podłączyć:

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;
Shmil The Cat
źródło
Otrzymuję: „brak sigar-x86-winnt.dll w java.library.path” Niestety wymaga to pobrania dodatkowej biblioteki DLL, która nie jest tak naprawdę możliwa w środowisku korporacyjnym (nie mam kontroli nad systemem CI). Tak źle ... W każdym razie dzięki.
uthomas
@uthomas Wydaje mi się, że nadal masz kontrolę nad pakietem wdrożeniowym aplikacji, jeśli tak jest, zawsze możesz zapewnić natywne biblioteki DLL / SO Sigar w pobliżu plików JAR i ustawić ścieżkę biblioteki JAVA pragmatycznie (za pomocą prostego hacka możesz na to wpłynąć nawet po app została uruchomiona przez JVM).
Shmil The Cat
2

Uporządkowanie odpowiedzi wskazanej przez Davida Santamarię:

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

To jest nadal przedmiotem wyścigu wskazane przez user207421 w komentarzach do Dawida Santamaria odpowiedzi (coś może chwycić portu po to metoda zamyka ServerSocketi DatagramSocketi wraca).

Luke Hutchison
źródło
1

W moim przypadku pomogło to przy próbie połączenia się z portem - jeśli usługa jest już obecna, to odpowiadała.

    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);              
    }
Ivan
źródło
1
Nie to, o co proszono.
Markiz Lorne
0

W moim przypadku musiałem użyć klasy 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;
    }
}

Nie zapomnij najpierw zaimportować

import java.net.DatagramSocket;
import java.net.BindException;
import java.net.SocketException;
kmchmk
źródło
Działa to w przypadku portów UDP. Wydaje się, że pytanie dotyczy portów TCP.
Markiz Lorne
-2

Próbowałem czegoś takiego i działało naprawdę dobrze

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

                 }
mayu jadhv
źródło
1
Nie to, o co proszono.
Markiz Lorne
A także wyciek z gniazda.
Markiz Lorne