Como encontrar uma porta disponível?

194

Eu quero iniciar um servidor que ouça uma porta. Eu posso especificar a porta explicitamente e funciona. Mas eu gostaria de encontrar uma porta de maneira automática. A este respeito, tenho duas perguntas.

  1. Em qual intervalo de números de porta devo procurar? (Eu usei as portas 12345, 12346 e 12347 e estava tudo bem).

  2. Como posso descobrir se uma determinada porta não está ocupada por outro software?

romano
fonte

Respostas:

277

Se você não se importa com a porta usada, especifique uma porta 0 para o construtor ServerSocket e ela ouvirá qualquer porta livre.

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

Se você deseja usar um conjunto específico de portas, provavelmente a maneira mais fácil é iterá-las até que funcione. Algo assim:

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

Pode ser usado assim:

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");
}
Graham Edgecombe
fonte
2
Ao usar o novo ServerSocket (0), tome cuidado para fechá-lo! Baseado em javasourcecode.org/html/open-source/eclipse/eclipse-3.5.2/org/... , ligeiramente adaptado na minha gist.github.com/3429822
vorburger
9
@vorburger, fazê-lo dessa maneira é propenso a condições de corrida. É melhor apenas ouvir no soquete do servidor imediatamente, em vez de abri-lo para encontrar uma porta livre, fechá-la novamente e depois abrir novamente na porta livre (quando houver uma pequena chance de que alguma outra coisa esteja escutando agora) )
Graham Edgecombe
7
concordou, mas depende do caso de uso exato: no meu caso, eu precisava encontrar um número de porta livre para entregá-lo em alguma API (por exemplo, um iniciador Jetty incorporado, para testes) - a respectiva API deseja um número de soquete - ainda não soquete do servidor aberto. Então depende.
precisa saber é o seguinte
4
As APIs razoáveis ​​do @vorburger aceitarão zero como um número de porta válido para escutar e, em seguida, informarão a porta real que está sendo ouvida. No entanto, não há muitas APIs razoáveis: muitos programas testam especificamente a passagem de 0 portas e a recusam ( ssh -D 127.0.0.0:0 ...? Não!), O que é realmente frustrante. Tivemos que corrigir várias bibliotecas / programas para usá-los.
Joker_vD
2
@vorburger Ao usar qualquer porta, deve-se tomar cuidado para fechá-la. new ServerSocket(0)não é diferente a esse respeito. Não há nada no seu primeiro link sobre isso. Seu segundo link apenas exibe más práticas. Depois de construí- ServerSocketlo, use -o, não feche -o e tente reutilizar o mesmo número de porta. Tudo isso é uma completa perda de tempo, além de estar vulnerável a problemas na janela de tempo.
Marquês de Lorne
57

Se você passar 0 como o número da porta para o construtor do ServerSocket, ele alocará uma porta para você.

Maurice Perry
fonte
Sim, acho que é a solução mais elegante, mas por alguns motivos, não funciona no meu caso. Mas ainda preciso descobrir o que exatamente está errado.
Roman
1
@ Roman: Por que não funciona? Atualize sua pergunta para incluir isso (ou as pessoas continuarão sugerindo) e explique por que essa solução falha para você.
FrustratedWithFormsDesigner
2
Como encontro esta porta do cliente? ;)
ed22
34

A partir do Java 1.7, você pode usar try-with-resources como este:

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

    }
  }

Se você precisar encontrar uma porta aberta em uma interface específica, verifique a documentação do ServerSocket para construtores alternativos.

Aviso: Qualquer código que utilize o número da porta retornado por este método está sujeito a uma condição de corrida - um processo / encadeamento diferente pode ser vinculado à mesma porta imediatamente após o fechamento da instância ServerSocket.

Andrei Savu
fonte
1
Isso pode não funcionar se você não definir SO_REUSEADDR. E há uma condição de corrida, mas é difícil consertar isso.
David Ehrmann
Isso pode não funcionar se você tentar abrir outra posteriormente ServerSocketcom o mesmo número de porta, pois pode ter sido obtido enquanto isso. Por que você não apenas devolve o real em ServerSocketvez de fechá-lo permanece um mistério.
Marquês de Lorne
5
@EJP Às vezes, o código de terceiros aceita uma porta, mas não o soquete.
Captain Man
@CaptainMan Claro, mas nesses casos a porta certamente é assumida como pré-alocada. Uma porta alocada dinamicamente deve ser fornecida aos clientes por algum outro mecanismo, como um serviço de nomenclatura ou uma transmissão ou transmissão múltipla. Toda a questão aqui está malformada.
Marquês de Lorne
@ user207421 Não é possível alterar o código de terceiros para aceitar um em ServerSocketvez de um intpara a porta.
Capitão Man
30

Segundo a Wikipedia , você deve usar portas 49152para 65535se você não precisa de uma porta 'bem conhecida'.

No AFAIK, a única maneira de determinar se uma porta está em uso é tentar abri-la.

Andy Johnson
fonte
6
+1. Como a Wikipedia nem sempre é a fonte de verdade / fatos absolutos, pensei que seria útil observar que, a referência para essa página da Wikipedia vem de "IANA (Internet Assigned Numbers Authority)" [Nome do serviço e número da porta do protocolo de transporte Página do Registro ( iana.org/assignments/service-names-port-numbers/… ", com base na RFC 6335 - Seção 6 (por exemplo, referência" Sólida "neste caso! :)).
OzgurH
25

Se você precisar no intervalo, use:

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;
    }
}
Serhii Bohutskyi
fonte
estava pensando em como verificar uma porta e depois desvincular-se dela. Obrigado SergeyB!
Vlad Ilie
5
Se todas as portas do intervalo [de, a) estiverem em uso, esse código fará um loop infinito (ou pelo menos até que uma dessas portas fique livre). Se você fizer uma varredura seqüencial em vez de escolher portas no intervalo aleatoriamente, poderá evitar essa possibilidade (basta lançar uma exceção quando chegar ao final do intervalo sem encontrar uma porta livre). Se você realmente precisar escolher portas aleatoriamente, precisará de um conjunto para acompanhar as portas que você tentou até agora e, em seguida, solte um erro quando o tamanho desse conjunto for igual a - de.
Simon Kissane
A porta 0 é consideravelmente mais simples e não requer a criação de duas ServerSocketsno caso de sucesso, e não sofre com os problemas da janela de tempo desse código.
Marquês de Lorne
11

O SDK do Eclipse contém uma classe SocketUtil , que faz o que você deseja. Você pode dar uma olhada no código fonte do git .

jopa
fonte
2
Olhando para o código do Eclipse, eles fazem a mesma coisa que a resposta de Graham Edgecombe
KevinL
6

Isso funciona para mim no Java 6

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

Recentemente, lançou uma pequena biblioteca para fazer exatamente isso com testes em mente. A dependência do Maven é:

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

Uma vez instalado, é possível obter números de porta gratuitos através de:

int port = FreePortFinder.findFreeLocalPort();
Alex Panov
fonte
4

Se o servidor iniciar, esse soquete não foi usado.

EDITAR

Algo como:

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));
}
OscarRyz
fonte
Não sei quem votou contra você, mas eu votei em você. Você pode configurar uma função recursiva para tentar configurar o ServerSocket e, se você receber uma IOException (ou o que for), tente novamente até que ela obtenha uma porta com êxito.
jonescb
Eu acho que é melhor verificar se uma porta está disponível e tentar começar a ouvir essa porta. Não parece elegante começar algo e descobrir que há algum problema e tentar resolvê-lo.
Roman
2
@ Roman bem, sim, isso seria melhor, exceto pelo fato de não haver um método para saber se uma porta está disponível. Se new ServerSocket(0)não está funcionando para o seu, a alternativa é esta. Eu acho que há 85% de possibilidades que você acaba usando minha sugestão.
OscarRyz
Esse código continua abrindo e vazando ServerSocketspara sempre e mantém apenas o último que foi bem-sucedido. Precisa de uma breakdeclaração.
Marquês de Lorne
4

Se você deseja criar seu próprio servidor usando a ServerSocket, basta escolher uma porta gratuita para você:

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

Outras implementações de servidor geralmente têm suporte semelhante. O Jetty, por exemplo, escolhe uma porta livre, a menos que você a defina explicitamente:

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

Pode não ajudar muito, mas na minha máquina (Ubuntu) eu tenho um arquivo / etc / services no qual pelo menos as portas usadas / reservadas por alguns dos aplicativos são fornecidas. Essas são as portas padrão para esses aplicativos.

Não há garantias de que eles estejam em execução, apenas as portas padrão que esses aplicativos usam (portanto, você não deve usá-los, se possível).

Existem pouco mais de 500 portas definidas, cerca de metade UDP e metade TCP.

Os arquivos são criados usando as informações da IANA. Consulte Números de porta atribuídos pela IANA .

extraneon
fonte
existe uma lista semelhante (e mais completa, IIRC) que faz parte do nmap. 1
rmeador
3

Se você estiver usando o Spring, a resposta fornecida por Michail Nikolaev é a mais simples e mais limpa, e a IMO deve ser votada. Apenas para fins de conveniência, adicionarei um exemplo embutido usando o método Springframwework SocketUtils .findAvailableTcpPort () :

int randomAvailablePort = SocketUtils.findAvailableTcpPort();

É tão fácil quanto isso, apenas uma linha :). Obviamente, a classe Utils oferece muitos outros métodos interessantes, sugiro dar uma olhada nos documentos.

Gerardo Roza
fonte
1

Usando a classe 'ServerSocket', podemos identificar se determinada porta está em uso ou livre. ServerSocket fornece um construtor que usa um número inteiro (que é o número da porta) como argumento e inicializa o soquete do servidor na porta. Se o ServerSocket lançar qualquer exceção de E / S, podemos assumir que essa porta já está em uso.

O fragmento a seguir é usado para obter todas as portas disponíveis.

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

         }
}

Link de referência .

Hari Krishna
fonte
0

se a porta estiver ocupada por outro software, o código emitirá uma IOException

Cezar Terzian
fonte