32

非常に単純なポート スキャナーを作成しましたが、実行速度が遅すぎるため、スキャンを高速化する方法を探しています。これが私のコードです:

public boolean portIsOpen(String ip, int port, int timeout) {
    try {
        Socket socket = new Socket();
        socket.connect(new InetSocketAddress(ip, port), timeout);
        socket.close();
        return true;
    } catch (Exception ex) {
        return false;
    }
}

このコードは、特定のポートが特定の IP で開いているかどうかをテストします。タイムアウトには最小値を使用しました200。これを下げると、ポートをテストするのに十分な時間がないためです。

うまく機能しますが、0 から 65535 までをスキャンするには時間がかかりすぎます。5 分以内に 0 から 65535 までをスキャンできる方法は他にありますか?

4

7 に答える 7

70

65536 個のポートごとに 200 ミリ秒が必要な場合 (最悪の場合、ファイアウォールがすべてをブロックしているため、すべてのポートでタイムアウトが発生します)、計算は非常に簡単です。13,000 秒、つまり約 3 時間必要です半分。

高速化するには、2 つの (非排他的な) オプションがあります。

  • タイムアウトを減らす
  • コードを並列化する

操作は I/O バウンドであるため (CPU バウンドとは対照的に、つまり、膨大な計算が完了するのではなく、I/O の待機に時間を費やします)、非常に多くのスレッドを使用できます。20から始めてみてください。3時間半を2人で割るので、最大で10分くらいが目安です。これは反対側に圧力をかけることを覚えておいてください。つまり、スキャンされたホストは、「不合理」または「奇妙な」パターンで巨大なネットワーク アクティビティを検出し、スキャンを非常に簡単に検出できるようになります。

最も簡単な方法 (つまり、最小限の変更) は、ExecutorService および Future API を使用することです。

public static Future<Boolean> portIsOpen(final ExecutorService es, final String ip, final int port, final int timeout) {
  return es.submit(new Callable<Boolean>() {
      @Override public Boolean call() {
        try {
          Socket socket = new Socket();
          socket.connect(new InetSocketAddress(ip, port), timeout);
          socket.close();
          return true;
        } catch (Exception ex) {
          return false;
        }
      }
   });
}

次に、次のようなことができます。

public static void main(final String... args) {
  final ExecutorService es = Executors.newFixedThreadPool(20);
  final String ip = "127.0.0.1";
  final int timeout = 200;
  final List<Future<Boolean>> futures = new ArrayList<>();
  for (int port = 1; port <= 65535; port++) {
    futures.add(portIsOpen(es, ip, port, timeout));
  }
  es.shutdown();
  int openPorts = 0;
  for (final Future<Boolean> f : futures) {
    if (f.get()) {
      openPorts++;
    }
  }
  System.out.println("There are " + openPorts + " open ports on host " + ip + " (probed with a timeout of " + timeout + "ms)");
}

どのポートが開いているか(上記の例のようにいくつだけでなく)知る必要がある場合は、関数の戻り値の型を に変更する必要があります。これは、ポートとスキャンの結果を保持するFuture<SomethingElse>場所です。SomethingElseお気に入り:

public final class ScanResult {
  private final int port;
  private final boolean isOpen;
  // constructor
  // getters
}

次に、最初のスニペットで に変更Booleanし、単にorの代わりにorを返しますScanResultnew ScanResult(port, true)new ScanResult(port, false)truefalse

編集: 実際、私はちょうど気付きました: この特定のケースでは、結果 + ポートを保持するために ScanResult クラスは必要なく、どのポートが開いているかはまだわかっています。order であるListに先物を追加し、後でそれらを追加したのと同じ順序で処理するため、処理しているポートを知るために各反復でインクリメントするカウンターを持つことができます. しかし、ねえ、これは完全かつ正確であるためです. それをやろうとしないでください、それは恐ろしいです、私はこれについて考えたことをほとんど恥じています... ScanResult オブジェクトを使用すると、はるかにクリーンになります。たとえば、a を使用してスキャナーを改善します。CompletionService

于 2012-07-18T17:34:51.690 に答える
4

スキャンの並列化とは別に、http: //nmap.org/nmap_doc.htmlで説明されているもの (TCP SYN および TCP FIN スキャン) のような、より高度なポート スキャン手法を使用できます。実装の VB コードはここにあります: http://h.ackack.net/spoon-worlds-fastest-port-scanner.html

ただし、これらの手法を使用するには、生の TCP/IP ソケットを使用する必要があります。これにはRockSawライブラリを使用する必要があります。

于 2012-07-18T17:49:40.513 に答える
2

Nmap オプションを使用することに決め、Java を使い続けたい場合は、SourceForge.net の Nmap4j を参照してください。

これは、Nmap を Java アプリに統合できるシンプルな API です。

于 2012-07-19T19:35:48.907 に答える