Soquetes: descubra a disponibilidade da porta usando Java

123

Como determino programaticamente a disponibilidade de uma porta em uma determinada máquina usando Java?

ou seja, dado um número de porta, determine se ele já está sendo usado ou não ?.

user54075
fonte
2
Embora esta pergunta é sobre como saber se uma determinada porta já está em uso, você pode ter desembarcado aqui tentando encontrar uma maneira de obter um número de porta livre, que stackoverflow.com/questions/2675362/... (com link para a minha essência .github.com / 3429822 ) cobre melhor.
precisa saber é o seguinte
Para qual propósito? Se você quiser apenas encontrar um soquete vazio para escutar, especifique zero. Qualquer técnica de exploração é susceptível de problemas-janela de tempo:. A porta pode ser vago quando você digitalizar e ocupada quando você vai a reivindicação em
Marquês de Lorne

Respostas:

91

Esta é a implementação proveniente do projeto 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;
}

Eles também estão verificando o DatagramSocket para verificar se a porta está disponível em UDP e TCP.

Espero que isto ajude.

David Santamaria
fonte
5
Uma boa variante disso, se você tiver MINA, é AvailablePortFinder.getNextAvailable (1024), que fornece a próxima porta sem privilégios.
Alain O'Dea
9
Isso não pega tudo no entanto. Fui mordido por um nginx em execução no TCP 8000, enquanto a rotina acima informa 8000 como disponível. Não faço ideia do porquê - suspeito que o nginx faz algumas coisas sorrateiras (isso é no OS X). A solução para mim é fazer o acima e também abrir um novo soquete ("localhost", porta) e retornar false se não houver uma exceção. Pensamentos?
Parcialmente nublado
2
Mesmo problema no Windows, tentando detectar se o servidor SMTP papercut está em execução. Uma solução em que você abre uma conexão a uma porta, em vez de tentar vinculá-la (como sugerido pelo comentário da Partly Clodys e pela resposta de Ivan) parece funcionar mais confiável.
Stian
4
Interessante. A chamada para setReuseAddress () é completamente inútil após o soquete já estar vinculado e também completamente contrária à finalidade do código.
Marquês de Lorne
3
Este código está sujeito a problemas na janela de temporização. Se o objetivo é criar uma ServerSocketescuta para alguma porta, é isso que ela deve fazer e retornar a ServerSocket. Criá-lo e, em seguida, fechá-lo e devolvê-lo não garante que um subseqüente ServerSocketpossa ser criado usando essa porta. O mesmo vale para DatagramSocket.
Marquês de Lorne
41

No Java 7, você pode usar o try-with-resource para obter um código mais compacto:

private static boolean available(int port) {
    try (Socket ignored = new Socket("localhost", port)) {
        return false;
    } catch (IOException ignored) {
        return true;
    }
}
eivindw
fonte
15
Isso não testa se a porta está disponível. Ele testa se está no estado LISTEN, se o endereço IP está acessível etc.
Marquês de Lorne
1
Além disso, este teste é bem lento (quase um segundo por porta).
JMax
ele não deve retornar false quando um IOException é capturado?
Baroudi Safwen 6/08/19
@BaroudiSafwen Depende inteiramente de qual é realmente a exceção. Pois ConnectException: 'connection refused', sim, ele deve retornar falso. Para tempos limite, nada que pudesse retornar seria válido, pois a resposta real não é conhecida. É por isso que essa técnica é inútil para esse fim.
Marquês de Lorne
36

Parece que a partir do Java 7, a resposta de David Santamaria não funciona mais de maneira confiável. Parece que você ainda pode usar um soquete de maneira confiável para testar a conexão.

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
fonte
Quero verificar se um servidor proxy está disponível para fazer chamadas em seu nome.
Dejell 01/01
1
A resposta de @ DavidSantamaria nunca funcionou. Nada a ver com o Java 7.
Marquis of Lorne
35

Se você não está muito preocupado com o desempenho, sempre pode tentar ouvir uma porta usando a classe ServerSocket . Se ele lança uma exceção, as probabilidades estão sendo usadas.

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 tudo o que você está tentando fazer é selecionar uma porta gratuita, new ServerSocket(0)ela encontrará uma para você.

Spencer Ruport
fonte
Use finalmente para limpeza (tente {...} pegar {...} finalmente {if (socket! = Null) socket.close ();} #
Hosam Aly
Você também pode definir "portTaken = (socket == null);" no final, em vez de fazê-lo na captura.
Hosam Aly 12/01/2009
1
Não senti que isso se enquadrasse no escopo pretendido da pergunta do autor.
Spencer Ruport
1
Existe uma maneira de fazer isso sem tentar / pegar? Eu não me importaria de usar nenhuma porta disponível para meus propósitos, mas iterar sobre os números de porta e tentar capturar cada uma delas até encontrar uma gratuita é um pouco inútil. Não existe um método 'nativo booleano disponível (int)' em algum lugar, que apenas verifica?
Oren Shalev
27
O novo SeverSocket (0) selecionará automaticamente uma porta livre.
Spencer Ruport
10

A solução a seguir é inspirada na implementação do Spring-core do SocketUtils (licença Apache).

Comparado a outras soluções Socket(...), é bastante rápido (testando 1000 portas TCP em menos de um segundo):

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
fonte
3

As soluções baseadas em soquete try / catch podem não gerar resultados precisos (o endereço do soquete é "localhost" e, em alguns casos, a porta pode ser "ocupada" não pela interface de loopback e, pelo menos no Windows, vi esse teste falhar, ou seja o prot falsamente declarado como disponível).

Há uma biblioteca legal chamada SIGAR , o seguinte código pode conectá-lo:

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
fonte
Recebo: "no sigar-x86-winnt.dll em java.library.path" Infelizmente, é necessário o download de alguma dll adicional que não é realmente viável no ambiente corporativo (não tenho controle sobre o sistema de CI). Tão ruim .... Obrigado de qualquer maneira.
Uthomas 18/04
@uthomas Eu acho que você ainda tem controle sobre o pacote de implantação de aplicativos, se esse for o caso, você sempre pode fornecer DLL / SOs de tempo de execução nativo Sigar perto dos seus JARs e definir o caminho da lib JAVA de forma pragmática aplicativo lançado pela JVM).
Shmil The Cat
2

Uma limpeza da resposta apontada por 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;
    }
}

Isso ainda está sujeita a uma condição de corrida apontado por user207421 nos comentários a resposta de David Santamaria (algo poderia pegar o porto após esse método fecha o ServerSockete DatagramSockete volta).

Luke Hutchison
fonte
1

No meu caso, ajudou a tentar conectar-se à porta - se o serviço já estiver presente, ele responderia.

    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
fonte
1
Não foi o que foi pedido.
Marquês de Lorne
0

No meu caso, eu tive que usar a 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;
    }
}

Não se esqueça de importar primeiro

import java.net.DatagramSocket;
import java.net.BindException;
import java.net.SocketException;
kmchmk
fonte
Isso funciona para portas UDP. A pergunta parece ser sobre portas TCP.
Marquês de Lorne
-2

Eu tentei algo assim e funcionou muito bem comigo

            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
fonte
1
Não foi o que foi pedido.
Marquês de Lorne
E também um vazamento de soquete.
Marquês de Lorne