Socket: scopri la disponibilità delle porte utilizzando Java


123

Come si determina a livello di codice la disponibilità di una porta in una determinata macchina utilizzando Java?

vale a dire dato un numero di porta, determinare se è già in uso o no ?.


2
Sebbene questa domanda riguardi come sapere se una determinata porta è già in uso, potresti essere atterrato qui cercando di trovare un modo per ottenere un numero di porta gratuito, che stackoverflow.com/questions/2675362/… (con link al mio gist .github.com / 3429822 ) copre meglio.
vorburger

Per quale scopo? Se vuoi solo trovare un socket libero da ascoltare, specifica zero. Qualsiasi tecnica di scansione è soggetta a problemi di finestra temporale: la porta può essere vuota quando esegui la scansione e occupata quando vai a reclamare.
Marchese di Lorne

Risposte:


91

Questa è l' implementazione proveniente dal progetto Apache camel :

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

Stanno controllando anche DatagramSocket per verificare se la porta è disponibile in UDP e TCP.

Spero che questo ti aiuti.


5
Una bella variante di questo se hai MINA è AvailablePortFinder.getNextAvailable (1024) che ti dà la prossima porta non privilegiata.
Alain O'Dea

9
Tuttavia, questo non cattura tutto. Sono stato morso da un nginx in esecuzione su TCP 8000 mentre la routine precedente riporta 8000 come disponibile. Non ho idea del perché: sospetto che nginx faccia cose subdole (questo è su OS X). La soluzione alternativa per me è fare quanto sopra e anche aprire un nuovo Socket ("localhost", port) e poi restituire false se non otteniamo un'eccezione. Pensieri?
Parzialmente nuvoloso

2
Stesso problema su Windows che tenta di rilevare se il server SMTP papercut è in esecuzione. Una soluzione in cui si apre una connessione a una porta, piuttosto che provare a collegarsi a una porta (come suggerito dal commento di Partly Clodys e dalla risposta di Ivan) sembra funzionare in modo più affidabile.
stian

4
Interessante. La chiamata a setReuseAddress () è completamente inutile dopo che il socket è già associato, ed è anche completamente contraria allo scopo del codice.
Marchese di Lorne

3
Questo codice è soggetto a problemi di finestra temporale. Se l'obiettivo è creare un ServerSocketascolto per qualche porta, è quello che dovrebbe fare e restituire il file ServerSocket. Crearlo e poi chiuderlo e restituirlo non garantisce che un successivo ServerSocketpossa essere creato utilizzando quella porta. Idem per DatagramSocket.
Marchese di Lorne

41

Per Java 7 puoi usare try-with-resource per un codice più compatto:

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

15
Questo non verifica se at port è disponibile. Verifica se è nello stato ASCOLTO, se l'indirizzo IP è raggiungibile, ecc.
Marchese di Lorne

1
Inoltre questo test è piuttosto lento (quasi un secondo per porta).
JMax

non dovrebbe restituire false quando viene rilevata una IOException?
Baroudi Safwen

@BaroudiSafwen Dipende interamente da quale sia effettivamente l'eccezione. Perché ConnectException: 'connection refused'sì, dovrebbe restituire falso. Per i timeout, nulla che potrebbe restituire sarebbe valido, poiché la risposta effettiva non è nota. Ecco perché questa tecnica è inutile per questo scopo.
Marchese di Lorne

36

Sembra che a partire da Java 7, la risposta di David Santamaria non funzioni più in modo affidabile. Tuttavia, sembra che tu possa ancora utilizzare in modo affidabile un Socket per testare la connessione.

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

Voglio controllare se è disponibile un server proxy per effettuare chiamate per suo conto.
Dejell

1
La risposta di @ DavidSantamaria non ha mai funzionato. Niente a che vedere con Java 7.
Marchese di Lorne

35

Se non sei troppo interessato alle prestazioni, puoi sempre provare ad ascoltare su una porta utilizzando la classe ServerSocket . Se genera un'eccezione, le probabilità sono che venga utilizzato.

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

EDIT: Se tutto ciò che stai cercando di fare è selezionare una porta libera, new ServerSocket(0)ne troverai una per te.


Usa infine per la pulizia (prova {...} cattura {...} finalmente {if (socket! = Null) socket.close ();}
Hosam Aly,

Puoi anche impostare "portTaken = (socket == null);" alla fine invece di farlo nella presa.
Hosam Aly

1
Non ho ritenuto che rientrasse nello scopo previsto della domanda dell'autore.
Spencer Ruport

1
C'è un modo per farlo senza try / catch? Non mi dispiacerebbe usare nessuna porta disponibile per i miei scopi, ma iterare sui numeri di porta e provare / catturarli su ciascuno finché non ne trovo uno libero è un po 'uno spreco. Non c'è un metodo "native boolean available (int)" da qualche parte, che controlla?
Oren Shalev,

27
new SeverSocket (0) selezionerà automaticamente una porta libera.
Spencer Ruport

10

La seguente soluzione è ispirata all'implementazione SocketUtils di Spring-core (licenza Apache).

Rispetto ad altre soluzioni che lo utilizzano Socket(...)è piuttosto veloce (testare 1000 porte TCP in meno di un secondo):

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

Le soluzioni basate su socket try / catch potrebbero non fornire risultati accurati (l'indirizzo del socket è "localhost" e in alcuni casi la porta potrebbe essere "occupata" non dall'interfaccia di loopback e almeno su Windows ho visto che questo test fallisce, ad es. il prot falsamente dichiarato disponibile).

C'è una bella libreria chiamata SIGAR , il seguente codice può collegarti:

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;

Ottengo: "no sigar-x86-winnt.dll in java.library.path" Sfortunatamente richiede il download di alcune dll aggiuntive che non sono realmente utilizzabili in ambiente aziendale (non ho alcun controllo sul sistema CI). Così male .... Grazie comunque.
uthomas

@uthomas Immagino che tu abbia ancora il controllo sul pacchetto di distribuzione della tua app, se questo è il caso puoi sempre fornire DLL / SO di runtime nativi Sigar vicino ai tuoi JAR e impostare pragmaticamente il percorso della libreria JAVA (tramite un semplice hack puoi influenzarlo anche dopo il tuo app è stata lanciata dalla JVM).
Shmil The Cat

2

Una pulizia della risposta segnalata da 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;
    }
}

Questo è ancora soggetto a una condizione di gara segnalata dall'utente207421 nei commenti alla risposta di David Santamaria (qualcosa potrebbe afferrare il port dopo che questo metodo chiude ServerSockete DatagramSockete ritorna).


1

Nel mio caso è stato utile provare a connettersi alla porta: se il servizio è già presente, risponderebbe.

    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
Non quello che è stato chiesto.
Marchese di Lorne

0

Nel mio caso ho dovuto usare la classe 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;
    }
}

Non dimenticare di importare prima

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

Questo funziona per le porte UDP. La domanda sembra riguardare le porte TCP.
Marchese di Lorne

-2

Ho provato qualcosa di simile e ha funzionato molto bene con me

            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
Non quello che è stato chiesto.
Marchese di Lorne

E anche una perdita nella presa.
Marchese di Lorne
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.