使用 Java 扫描端口的最快方法 [英] Fastest way to scan ports with Java

查看:58
本文介绍了使用 Java 扫描端口的最快方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我制作了一个非常简单的端口扫描器,但运行速度太慢,所以我正在寻找一种方法来使其扫描速度更快.这是我的代码:

I made a very simple port scanner, but it runs too slow, so I'm looking for a way to make it scan faster. Here is my code:

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 的最小值,因为当我降低时,它没有足够的时间来测试端口.

This code tests if a specific port is open on a specific ip. For timeout I used a minimum value of 200 because when I go lower it doesn't have enough time to test the port.

效果很好,但是从 0 扫描到 65535 需要太多时间.有没有其他方法可以在不到 5 分钟的时间内从 0 扫描到 65535?

推荐答案

如果 65536 个端口中的每一个都需要 200 毫秒(在最坏的情况下,防火墙会阻止所有内容,从而使每个端口都超时),数学很简单:你需要 13k 秒,或者大约 3 个半小时.

If you need 200ms for each of the 65536 ports (in the worst case, a firewall is blocking everything, thus making you hit your timeout for every single port), the maths is pretty simple: you need 13k seconds, or about 3 hours and a half.

您有 2 个(非排他性)选项可以加快速度:

You have 2 (non-exclusive) options to make it faster:

  • 减少超时时间
  • 并行化您的代码

由于操作是 I/O 绑定的(与 CPU 绑定相反——也就是说,你花时间等待 I/O,而不是等待一些巨大的计算完成),你可以使用很多很多线程.尝试从 20 开始.他们会在其中划分 3 个半小时,因此预计最长的时间约为 10 分钟.请记住,这会给另一方带来压力,即被扫描的主机会看到大量具有不合理"或奇怪"模式的网络活动,从而使扫描非常容易被检测到.

Since the operation is I/O bound (in contrast to CPU bound -- that is, you spend time waiting for I/O, and not for some huge calculation to complete), you can use many, many threads. Try starting with 20. They would divide the 3 hours and a half among them, so the maximum expected time is about 10 minutes. Just remember that this will put pressure on the other side, ie, the scanned host will see huge network activity with "unreasonable" or "strange" patterns, making the scan extremely easy to detect.

最简单的方法(即更改最少)是使用 ExecutorService 和 Future API:

The easiest way (ie, with minimal changes) is to use the ExecutorService and Future APIs:

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

然后,您可以执行以下操作:

Then, you can do something like:

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 将保存端口和扫描结果,例如:

If you need to know which ports are open (and not just how many, as in the above example), you'd need to change the return type of the function to Future<SomethingElse>, where SomethingElse would hold the port and the result of the scan, something like:

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

然后,将第一个片段中的 Boolean 更改为 ScanResult,并返回 new ScanResult(port, true)new ScanResult(port, false) 而不是 truefalse

Then, change Boolean to ScanResult in the first snippet, and return new ScanResult(port, true) or new ScanResult(port, false) instead of just true or false

实际上,我刚刚注意到:在这种特殊情况下,您不需要 ScanResult 类来保存结果 + 端口,并且仍然知道哪个端口是打开的.由于您将期货添加到有序列表,然后您按照添加它们的相同顺序处理它们,您可以有一个计数器,您可以在每次迭代时递增该计数器以了解您正在处理的端口.但是,嘿,这只是为了完整和精确.永远不要尝试这样做,这太可怕了,想到这个我很惭愧...使用 ScanResult 对象要干净得多,代码是更易于阅读和维护,并允许您稍后使用 CompletionService 来改进扫描器.

Actually, I just noticed: in this particular case, you don't need the ScanResult class to hold result + port, and still know which port is open. Since you add the futures to a List, which is ordered, and, later on, you process them in the same order you added them, you could have a counter that you'd increment on each iteration to know which port you are dealing with. But, hey, this is just to be complete and precise. Don't ever try doing that, it is horrible, I'm mostly ashamed that I thought about this... Using the ScanResult object is much cleaner, the code is way easier to read and maintain, and allows you to, later, for example, use a CompletionService to improve the scanner.

这篇关于使用 Java 扫描端口的最快方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆