ソケット:Javaを使用してポートの可用性を検出する


123

Javaを使用して、特定のマシンのポートの可用性をプログラムで判別するにはどうすればよいですか?

つまり、ポート番号を指定して、それがすでに使用されているかどうかを判断しますか?。


2
この質問は、指定されたポートが既に使用中である場合、あなたはどの、空いているポート番号を取得する方法を見つけようとしてここに上陸した可能性が知っている方法についてですがstackoverflow.com/questions/2675362/...(私へのリンクを要旨.github.com / 3429822)より適切にカバーします。
vorburger 2012

何のために?待機する空のソケットを検索するだけの場合は、ゼロを指定します。任意のスキャン技術のタイミング・ウィンドウの問題に傾向がある:あなたがスキャンして、あなたが請求項に行くときに占有するときポートが空いていることができる
ローン侯爵

回答:


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

彼らはDatagramSocketもチェックして、ポートがUDPおよびTCPで使用可能かどうかをチェックしています。

お役に立てれば。


5
MINAを使用している場合のこれの優れたバリアントは、次の非特権ポートを取得するAvailablePortFinder.getNextAvailable(1024)です。
Alain O'Dea

9
ただし、これはすべてをキャッチするわけではありません。上記のルーチンで8000が利用可能であると報告されている間、私はTCP 8000でnginxを実行していることに噛まれました。理由はわかりません-nginxが卑劣なことをしているのではないかと思います(これはOS X上にあります)。私の回避策は、上記を実行し、新しいSocket( "localhost"、port)を開いて、例外が発生しない場合にfalseを返すことです。考え?
部分的に曇り

2
Windowsで同じ問題があり、papercut SMTPサーバーが実行されているかどうかを検出しようとしています。ポートにバインドしようとするのではなく、ポートへの接続を開くソリューション(Partly ClodysのコメントとIvanの回答で示唆されている)は、より信頼できるように見えます。
stian

4
面白い。setReuseAddress()の呼び出しは、ソケットがバインドされた後は完全に無意味であり、コードの目的に完全に反しています。
ローンの侯爵2013年

3
このコードは、タイミングウィンドウの問題の影響を受けます。目的がServerSocketポートをリッスンすることを作成することである場合、それはそれが何をすべきかであり、を返しますServerSocket。それを作成してから閉じて返すと、ServerSocketそのポートを使用して後続を作成できるという保証はありません。同上DatagramSocket
ローン侯爵

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 2018

IOExceptionがキャッチされたときにfalseを返すべきではありませんか?
Baroudi Safwen

@BaroudiSafwenそれは完全に例外が実際に何であるかに依存します。の場合ConnectException: 'connection refused'、はい、falseを返します。タイムアウトの場合、実際の答えがわからないため、何も返されません。このため、この手法はこの目的には役に立たない。
ローン侯爵

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の答えはうまくいきませんでした。Java 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)することであるなら、あなたのためにそれを見つけます。


最終的にクリーンアップに使用(トライ{...}キャッチ{...}最後に{if(ソケット!= null)socket.close();}
Aly

「portTaken =(socket == null);」を設定することもできます。キャッチでそれを行う代わりに最後に。
Hosam Aly

1
それが著者の質問の意図する範囲内にあるとは感じませんでした。
Spencer Ruport 2009年

1
try / catchなしでそれを行う方法はありますか?目的に使用可能なポートを使用してかまいませんが空きポートが見つかるまでポート番号を繰り返し、各ポートを試行/キャッチすることは少し無駄です。チェックするだけの「ネイティブブール値available(int)」メソッドはどこにもありませんか?
Oren Shalev、

27
新しいSeverSocket(0)は自動的に空きポートを選択します。
スペンサールポート09年

10

次のソリューションは、Spring-core(Apacheライセンス)のSocketUtils実装から発想を得ています。

それを使用する他のソリューションと比較して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ではこのテストが失敗することがわかりました。つまり利用可能として誤って宣言されたプロト)。

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 2013

@uthomasアプリの展開パッケージを引き続き制御できると思います。これが当てはまる場合は、常にJARの近くにSigarネイティブランタイムDLL / SOを提供し、JAVA libパスを実用的に設定できます(単純なハックを介して、後でも影響を与えることができます)アプリがJVMによって起動されました)。
Shmil The Cat 2013

2

デビッド・サンタマリアが指摘した答えの整理:

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

(この方法は、閉じた後にポートをつかむことができ、何かこれはまだデビッド・サンタマリアの答えにコメントでuser207421が指摘競合状態にさらされるServerSocketDatagramSocket、リターン)。


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
求められたものではありません。
ローン侯爵2015

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ポートに関するもののようです。
ローンの侯爵

-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
求められたものではありません。
ローンの侯爵

また、ソケットリーク。
ローン侯爵
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.