소켓 : Java를 사용하여 포트 가용성 검색


123

Java를 사용하여 주어진 시스템에서 포트의 가용성을 프로그래밍 방식으로 어떻게 결정합니까?

즉, 포트 번호가 주어지면 이미 사용 중인지 여부를 결정합니까?.


2
이 질문은 특정 포트가 이미 사용 중인지 확인하는 방법에 관한 것이지만, 무료 포트 번호를 얻는 방법을 찾으려고 여기에 도착했을 수 있습니다. stackoverflow.com/questions/2675362/… (내 요점에 대한 링크 포함) .github.com / 3429822 ) 더 잘 다룹니다.
vorburger 2012-08-22

무슨 목적을 위해? 청취 할 빈 소켓을 찾으려면 0을 지정하십시오. 모든 스캐닝 기술은 타이밍 윈도우 문제에 취약합니다. 스캔 할 때 포트가 비어 있고 청구 할 때 점유 될 수 있습니다.
Marquis of Lorne

답변:


91

이것은 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;
}

포트가 UDP 및 TCP에서 사용 가능한지 확인하기 위해 DatagramSocket도 확인하고 있습니다.

도움이 되었기를 바랍니다.


5
MINA가있는 경우 이것의 좋은 변형은 AvailablePortFinder.getNextAvailable (1024)로 다음 비 권한 포트를 가져옵니다.
Alain O'Dea 2011-06-27

9
그러나 이것이 모든 것을 잡는 것은 아닙니다. 위의 루틴은 8000을 사용 가능한 것으로보고하는 동안 TCP 8000에서 실행중인 nginx에 물 렸습니다. 이유는 모르겠습니다. nginx가 교활한 작업을 수행한다고 생각합니다 (OS X에 있음). 나를위한 해결 방법은 위의 작업을 수행하고 새 Socket ( "localhost", port)을 연 다음 예외가 발생하지 않으면 false를 반환하는 것입니다. 생각?
구름 많음

2
papercut SMTP 서버가 실행 중인지 감지하려는 Windows에서도 동일한 문제가 발생합니다. (Partly Clodys 의견과 Ivan의 답변에서 제안한 것처럼) 포트에 바인딩을 시도하는 대신 포트에 대한 연결을 여는 솔루션이 더 안정적으로 작동하는 것 같습니다.
스티

4
흥미 롭군. setReuseAddress ()에 대한 호출은 소켓이 이미 바인딩 된 후에는 완전히 무의미하며 코드의 목적과도 완전히 반대됩니다.
Marquis of Lorne의 2013 년

3
이 코드에는 타이밍 윈도우 문제가 있습니다. 목적이 ServerSocket어떤 포트를 청취하는 것이라면 그것이해야 할 일이고 ServerSocket. 생성 한 다음 닫고 반환하면 ServerSocket해당 포트를 사용하여 후속 항목 을 만들 수 있다는 보장이 없습니다. 동감을위한 DatagramSocket.
Marquis of Lorne

41

Java 7의 경우보다 간결한 코드를 위해 try-with-resource를 사용할 수 있습니다.

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

15
포트에서 사용할 수 있는지 여부는 테스트하지 않습니다. 그것은 상태는 IP 주소 등의 도달 여부에있다 LISTEN 여부 테스트
론 후작

1
또한이 테스트는 매우 느립니다 (포트 당 거의 1 초).
JMax

IOException이 잡히면 false를 반환하지 않습니까?
Baroudi Safwen

@BaroudiSafwen 그것은 전적으로 예외가 실제로 무엇인지에 달려 있습니다. 를 들어 ConnectException: 'connection refused', 네 false를 반환해야합니다. 시간 초과의 경우 실제 답변을 알 수 없으므로 반환 할 수있는 것은 유효하지 않습니다. 그렇기 때문에이 기술은이 목적에 쓸모가 없습니다.
Marquis of Lorne

36

Java 7부터 David Santamaria의 답변 이 더 이상 안정적으로 작동하지 않는 것 같습니다. 그러나 여전히 안정적으로 소켓을 사용하여 연결을 테스트 할 수있는 것 같습니다.

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

프록시 서버를 대신하여 전화를 걸 수 있는지 확인하고 싶습니다.
Dejell

1
@DavidSantamaria의 대답은 결코 효과가 없었습니다. 아무것도 자바 7을 할 수 없습니다
론의 후작

35

성능에 너무 관심이 없다면 항상 ServerSocket 클래스를 사용하여 포트에서 수신을 시도 할 수 있습니다. 예외 확률이 발생하면 사용중인 것입니다.

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

편집 : 당신이하려는 모든 것이 무료 포트를 선택하면 new ServerSocket(0)당신을 위해 하나를 찾을 것입니다.


정리를 위해 마지막으로 (시도 {...} 잡기 {...} 마지막으로 {! 경우 (소켓 = NULL) socket.close를 ();} 사용
호삼 알리

"portTaken = (socket == null);"을 설정할 수도 있습니다. 캐치에서하는 대신 끝에.
Hosam Aly

1
나는 그것이 저자의 질문의 의도 된 범위에 속한다고 생각하지 않았습니다.
Spencer Ruport

1
try / catch없이 할 수있는 방법이 있습니까? 내 목적을 위해 사용 가능한 포트를 사용 하는 것은 괜찮지 만 무료 포트를 찾을 때까지 포트 번호를 반복하고 각 포트를 시도 / 캐치하는 것은 약간 낭비입니다. 어딘가에 'native boolean available (int)'메소드가 있지 않습니까?
Oren Shalev

27
new SeverSocket (0)은 자동으로 사용 가능한 포트를 선택합니다.
Spencer Ruport

10

다음 솔루션은 SocketUtils에서 영감을 얻었습니다. Spring-core (Apache 라이센스) 구현에서 .

Socket(...)이를 사용 하는 다른 솔루션에 비해 매우 빠릅니다 (1 초 이내에 1000 개의 TCP 포트 테스트).

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

try / catch 소켓 기반 솔루션은 정확한 결과를 얻지 못할 수 있습니다 (소켓 주소는 "localhost"이고 경우에 따라 포트는 루프백 인터페이스가 아닌 "점유"될 수 있으며 적어도 Windows에서는이 테스트가 실패하는 것을 보았습니다. 사용 가능한 것으로 잘못 선언 된 prot).

SIGAR 라는 멋진 라이브러리가 있습니다 . 다음 코드로 연결할 수 있습니다.

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;

"java.library.path에 sigar-x86-winnt.dll이 없습니다."불행히도 엔터프라이즈 환경에서 실제로 실행되지 않는 추가 dll을 다운로드해야합니다 (CI 시스템을 제어 할 수 없음). 너무 안좋아 .. 어쨌든 고마워.
uthomas

@uthomas 나는 여전히 앱 배포 패키지를 제어 할 수 있다고 생각합니다.이 경우 JAR 근처에 Sigar 네이티브 런타임 DLL / SO를 제공하고 JAVA lib 경로를 실용적으로 설정할 수 있습니다 (간단한 해킹을 통해 JVM이 앱을 시작했습니다).
Shmil The Cat

2

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

이것은 여전히 ​​David Santamaria의 답변에 대한 의견에서 user207421이 지적한 경쟁 조건의 영향을받습니다 (이 메서드가 ServerSocketand를 닫고 DatagramSocket반환 한 후에 뭔가 포트를 가져올 수 있음 ).


1

제 경우에는 포트에 연결을 시도하고 연결하는 데 도움이되었습니다. 서비스가 이미 있으면 응답 할 것입니다.

    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
요청한 것이 아닙니다.
Marquis of Lorne

0

제 경우에는 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;
    }
}

먼저 가져 오는 것을 잊지 마세요

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

이것은 UDP 포트에서 작동합니다. 질문은 TCP 포트에 관한 것 같습니다.
Marquis of Lorne

-2

나는 이것과 같은 것을 시도했고 그것은 나에게 정말 잘 작동했습니다.

            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
요청한 것이 아닙니다.
Marquis of Lorne

그리고 소켓 누출도 있습니다.
Marquis of Lorne
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.