Come trovare una porta disponibile?


195

Voglio avviare un server che ascolta una porta. Posso specificare esplicitamente la porta e funziona. Ma vorrei trovare una porta in modo automatico. A questo proposito ho due domande.

  1. In quale intervallo di numeri di porta devo cercare? (Ho usato le porte 12345, 12346 e 12347 e andava bene).

  2. Come posso sapere se una determinata porta non è occupata da un altro software?

Risposte:


281

Se non ti dispiace la porta utilizzata, specifica una porta 0 per il costruttore ServerSocket e ascolterà su qualsiasi porta libera.

ServerSocket s = new ServerSocket(0);
System.out.println("listening on port: " + s.getLocalPort());

Se si desidera utilizzare un set specifico di porte, il modo più semplice è probabilmente quello di scorrere attraverso di esse fino a quando non funziona. Qualcosa come questo:

public ServerSocket create(int[] ports) throws IOException {
    for (int port : ports) {
        try {
            return new ServerSocket(port);
        } catch (IOException ex) {
            continue; // try next port
        }
    }

    // if the program gets here, no port in the range was found
    throw new IOException("no free port found");
}

Potrebbe essere usato così:

try {
    ServerSocket s = create(new int[] { 3843, 4584, 4843 });
    System.out.println("listening on port: " + s.getLocalPort());
} catch (IOException ex) {
    System.err.println("no available ports");
}

2
Quando si utilizza il nuovo ServerSocket (0), prestare attenzione a chiuderlo! Basato su javasourcecode.org/html/open-source/eclipse/eclipse-3.5.2/org/… , leggermente adattato nel mio gist.github.com/3429822
vorburger il

9
@vorburger, farlo in quel modo è soggetto alle condizioni di gara. È meglio ascoltare immediatamente sul socket del server, piuttosto che aprirlo per trovare una porta libera, chiuderlo di nuovo e quindi riaprirne uno sulla porta libera (a quel punto c'è una piccola possibilità che qualcos'altro stia ascoltando su quello porto.)
Graham Edgecombe

7
concordato, ma dipende dal caso d'uso esatto: nel mio caso avevo bisogno di trovare un numero di porta gratuito per consegnarlo in qualche API (diciamo un iniziatore Jetty incorporato, per i test) - la rispettiva API vuole un numero socket - non già socket server aperto. Quindi dipende.
Vorburger,

4
Le API ragionevoli di @vorburger accetteranno zero come numero di porta valido per l'ascolto, quindi ti diranno la porta effettiva in ascolto. Hovewer, non ci sono molte API ragionevoli: molti programmi testano specificamente il passaggio di 0 porte e lo rifiutano ( ssh -D 127.0.0.0:0 ...? No!), Il che è davvero frustrante. Abbiamo dovuto patchare un certo numero di librerie / programmi per renderli utili a noi.
Joker_vD,

2
@vorburger Quando si utilizza una porta, fare attenzione a chiuderla. new ServerSocket(0)non è diverso da questo punto di vista. Non c'è nulla nel tuo primo link al riguardo. Il tuo secondo link mostra semplicemente scarsa pratica. Dopo averlo creato ServerSocket, dovresti usarlo , non chiuderlo e provare a riutilizzare lo stesso numero di porta. Tutto ciò è una completa perdita di tempo, oltre ad essere vulnerabile ai problemi della finestra dei tempi.
Marchese di Lorne,

57

Se passi 0 come numero di porta al costruttore di ServerSocket, assegnerà una porta per te.


Sì, penso che sia la soluzione più elegante, ma per alcuni motivi non funziona nel mio caso. Ma devo ancora scoprire cosa è esattamente sbagliato.
Romano,

1
@Roman: perché non funziona? Aggiorna la tua domanda per includere questo (o le persone continueranno a suggerirlo) e spiega perché questa soluzione non riesce per te.
FrustratedWithFormsDesigner

3
Come trovo questa porta dal client? ;)
ed22

34

A partire da Java 1.7 puoi usare try-with-resources in questo modo:

  private Integer findRandomOpenPortOnAllLocalInterfaces() throws IOException {
    try (
        ServerSocket socket = new ServerSocket(0);
    ) {
      return socket.getLocalPort();

    }
  }

Se è necessario trovare una porta aperta su un'interfaccia specifica, consultare la documentazione ServerSocket per i costruttori alternativi.

Avvertenza: qualsiasi codice che utilizza il numero di porta restituito da questo metodo è soggetto a una race condition: un processo / thread diverso può associarsi alla stessa porta immediatamente dopo la chiusura dell'istanza ServerSocket.


1
Questo potrebbe non funzionare se non impostato SO_REUSEADDR. E c'è una condizione di gara, ma è difficile risolverlo.
David Ehrmann,

Questo potrebbe non funzionare se successivamente si tenta di aprirne un altro ServerSocket con lo stesso numero di porta, poiché potrebbe essere stato eseguito nel frattempo. Perché non dovresti semplicemente restituire il vero ServerSocketinvece di chiuderlo rimane un mistero.
Marchese di Lorne,

5
@EJP A volte il codice di terze parti accetta una porta ma non il socket stesso.
Captain Man,

@CaptainMan Certo, ma in quei casi si presume che la porta sia preallocata. Una porta allocata dinamicamente deve essere fornita ai client da qualche altro meccanismo come un servizio di denominazione o una trasmissione o mulitcast. L'intera domanda qui è malformata.
Marchese di Lorne,

@ user207421 Non è possibile modificare il codice di terze parti per accettare un ServerSocketinvece di un intper la porta.
Captain Man,

30

Secondo Wikipedia , è necessario utilizzare le porte 49152a 65535se non hai bisogno di una porta 'ben noto'.

AFAIK l'unico modo per determinare se una porta è in uso è provare ad aprirla.


6
+1. Poiché Wikipedia non è sempre la fonte di verità / fatti assoluti, ho pensato che potrebbe essere utile notare che, il riferimento per quella pagina di Wikipedia proviene da "Internet Assigned Numbers Authority (IANA)" s "[Nome servizio e numero di porta del protocollo di trasporto Registro ( pagina iana.org/assignments/service-names-port-numbers/… ", basato su RFC 6335 - Sezione 6 (ovvero riferimento" solido "in questo caso! :)).
OzgurH,

25

Se è necessario nel raggio di azione utilizzare:

public int nextFreePort(int from, int to) {
    int port = randPort(from, to);
    while (true) {
        if (isLocalPortFree(port)) {
            return port;
        } else {
            port = ThreadLocalRandom.current().nextInt(from, to);
        }
    }
}

private boolean isLocalPortFree(int port) {
    try {
        new ServerSocket(port).close();
        return true;
    } catch (IOException e) {
        return false;
    }
}

mi chiedevo come controllare una porta e poi separarsi da essa. Grazie SergeyB!
Vlad Ilie,

5
Se tutte le porte nell'intervallo [da, a) sono in uso, questo codice verrà ripetuto all'infinito (o almeno fino a quando una di quelle porte diventa libera). Se si esegue una scansione sequenziale anziché selezionare casualmente le porte nell'intervallo, è possibile evitare questa possibilità (basta lanciare un'eccezione quando si arriva alla fine dell'intervallo senza trovare una porta libera). Se hai davvero bisogno di scegliere le porte a caso, allora hai bisogno di un set per tenere traccia delle porte che hai provato finora, e quindi generare un errore quando la dimensione di quel set è uguale a - da.
Simon Kissane,

La porta 0 è considerevolmente più semplice e non richiede la creazione di due ServerSocketsin caso di successo e non presenta i problemi della finestra di temporizzazione di questo codice.
Marchese di Lorne,


11

Eclipse SDK contiene una classe SocketUtil , che fa quello che vuoi. Potresti dare un'occhiata al codice sorgente di git .


2
Guardando il codice di Eclipse, fanno la stessa cosa della risposta di Graham Edgecombe
KevinL


6

Questo funziona per me su Java 6

    ServerSocket serverSocket = new ServerSocket(0);
    System.out.println("listening on port " + serverSocket.getLocalPort());

6

Di recente ho pubblicato una minuscola libreria per fare proprio questo con i test in mente. La dipendenza da Maven è:

<dependency>
    <groupId>me.alexpanov</groupId>
    <artifactId>free-port-finder</artifactId>
    <version>1.0</version>
</dependency>

Una volta installato, i numeri di porta liberi possono essere ottenuti tramite:

int port = FreePortFinder.findFreeLocalPort();

4

Se il tuo server si avvia, quel socket non è stato usato.

MODIFICARE

Qualcosa di simile a:

ServerSocket s = null ;

try { 
    s = new ServerSocket( 0 ); 
} catch( IOException ioe ){
   for( int i = START; i < END ; i++ ) try {
        s = new ServerSocket( i );
    } catch( IOException ioe ){}
}
// At this point if s is null we are helpless
if( s == null ) {
    throw new IOException(
       Strings.format("Unable to open server in port range(%d-%d)",START,END));
}

Non so chi ti abbia votato verso il basso, ma io ho votato per il backup. È possibile impostare una funzione ricorsiva per provare a configurare ServerSocket e, se si ottiene una IOException (o qualunque altra cosa sia), riprovare fino a quando non ottiene correttamente una porta.
jonescb,

Penso che sia meglio verificare se è disponibile una porta e quindi provare ad iniziare ad ascoltare questa porta. Non sembra elegante iniziare qualcosa e poi scoprire che ci sono dei problemi e quindi provare a risolverli.
Romano,

2
@Roman bene, sì, sarebbe meglio, tranne per il fatto che non esiste un metodo per sapere se è disponibile una porta. Se new ServerSocket(0)non funziona per te, l'alternativa è questa. Penso che ci sia l'85% delle possibilità che finisci per usare il mio suggerimento.
OscarRyz,

Questo codice continua ad aprirsi e perdere ServerSocketsper sempre e conserva solo l' ultimo che è riuscito. Ha bisogno di una breakdichiarazione.
Marchese di Lorne,

4

Se vuoi creare il tuo server usando un ServerSocket, puoi farlo scegliere una porta libera per te:

  ServerSocket serverSocket = new ServerSocket(0);
  int port = serverSocket.getLocalPort();

Altre implementazioni di server in genere hanno un supporto simile. Il molo, ad esempio, sceglie una porta libera a meno che tu non lo imposti esplicitamente:

  Server server = new Server();
  ServerConnector connector = new ServerConnector(server);
  // don't call: connector.setPort(port);
  server.addConnector(connector);
  server.start();
  int port = connector.getLocalPort();

3

Potrebbe non essere di grande aiuto, ma sulla mia macchina (Ubuntu) ho un file / etc / services in cui vengono fornite almeno le porte utilizzate / riservate da alcune app. Queste sono le porte standard per quelle app.

Nessuna garanzia che siano in esecuzione, solo le porte predefinite utilizzate da queste app (quindi non dovresti usarle se possibile).

Sono state definite più di 500 porte, circa metà UDP e metà TCP.

I file vengono creati utilizzando le informazioni di IANA, vedere I numeri di porta assegnati IANA .


esiste un elenco simile (e più completo, IIRC) che fa parte di nmap. +1
rmeador

3

Se stai usando Spring, la risposta fornita da Michail Nikolaev è la più semplice e pulita, e l'IMO dovrebbe essere votato. Solo per motivi di praticità, aggiungerò un esempio inline usando il metodo SocketUtils .findAvailableTcpPort () di Springframwework :

int randomAvailablePort = SocketUtils.findAvailableTcpPort();

È così facile, solo una riga :). Naturalmente, la classe Utils offre molti altri metodi interessanti, suggerisco di dare un'occhiata ai documenti.


1

Usando la classe 'ServerSocket' possiamo identificare se una determinata porta è in uso o libera. ServerSocket fornisce un costruttore che accetta un numero intero (che è il numero di porta) come argomento e inizializza il socket del server sulla porta. Se ServerSocket genera un'eccezione IO, possiamo supporre che questa porta sia già in uso.

Lo snippet seguente viene utilizzato per ottenere tutte le porte disponibili.

for (int port = 1; port < 65535; port++) {
         try {
                  ServerSocket socket = new ServerSocket(port);
                  socket.close();
                  availablePorts.add(port);
         } catch (IOException e) {

         }
}

Link di riferimento .


0

se la porta è occupata da un altro software, il codice genererà un'eccezione IOException

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.