소켓 : 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.