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