使用Java扫描端口的最快方法

我制作了非常简单的端口扫描程序,但运行速度太慢,所以我正在寻找方法让它扫描速度更快。 这是我的代码:

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?

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

您有2个(非独占)选项可以加快速度:

  • 减少你的超时
  • 对您的代码进行并列化

由于操作是I / O绑定(与CPU绑定相反 – 也就是说,您花费时间等待I / O,而不是完成一些大型计算), 您可以使用许multithreading 。 尝试从20开始。他们将其中的3个半小时分开, 因此最大预期时间约为10分钟 。 请记住,这会对另一方施加压力,即扫描的主机将看到具有“不合理”或“奇怪”模式的巨大网络活动,使扫描非常容易检测。

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

 public static Future portIsOpen(final ExecutorService es, final String ip, final int port, final int timeout) { return es.submit(new Callable() { @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> 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 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将保存端口和结果扫描,例如:

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

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

编辑:实际上,我只是注意到:在这种特殊情况下,您不需要ScanResult类来保存结果+端口,并且仍然知道哪个端口是打开的。 由于您将期货添加到已订购List中 ,并且稍后按照添加它们的相同顺序处理它们 ,您可以拥有一个计数器,您可以在每次迭代时递增以知道您正在处理哪个端口。 但是,嘿,这只是完整而准确的。 不要尝试这样做 ,它太可怕了,我对此感到羞耻…我使用ScanResult对象更清晰 ,代码更容易阅读和维护,并允许您以后,例如,使用CompletionService来改进扫描程序。

除了并行化扫描之外,您还可以使用更高级的端口扫描技术,例如这里解释的(TCP SYN和TCP FIN扫描): http : //nmap.org/nmap_doc.html 。 可以在此处找到实现的VB代码: http : //h.ackack.net/spoon-worlds-fastest-port-scanner.html

但是,要使用这些技术,您需要使用原始TCP / IP套接字。 您应该使用RockSaw库。

代码示例的灵感来自“Bruno Reis”

 class PortScanner { public static void main(final String... args) throws InterruptedException, ExecutionException { final ExecutorService es = Executors.newFixedThreadPool(20); final String ip = "127.0.0.1"; final int timeout = 200; final List> futures = new ArrayList<>(); for (int port = 1; port <= 65535; port++) { // for (int port = 1; port <= 80; port++) { futures.add(portIsOpen(es, ip, port, timeout)); } es.awaitTermination(200L, TimeUnit.MILLISECONDS); int openPorts = 0; for (final Future f : futures) { if (f.get().isOpen()) { openPorts++; System.out.println(f.get().getPort()); } } System.out.println("There are " + openPorts + " open ports on host " + ip + " (probed with a timeout of " + timeout + "ms)"); } public static Future portIsOpen(final ExecutorService es, final String ip, final int port, final int timeout) { return es.submit(new Callable() { @Override public ScanResult call() { try { Socket socket = new Socket(); socket.connect(new InetSocketAddress(ip, port), timeout); socket.close(); return new ScanResult(port, true); } catch (Exception ex) { return new ScanResult(port, false); } } }); } public static class ScanResult { private int port; private boolean isOpen; public ScanResult(int port, boolean isOpen) { super(); this.port = port; this.isOpen = isOpen; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public boolean isOpen() { return isOpen; } public void setOpen(boolean isOpen) { this.isOpen = isOpen; } } } 

对于Android,请尝试这个Awesome Library

https://github.com/stealthcopter/AndroidNetworkTools

如果您决定使用Nmap选项并希望继续使用Java,那么您应该在SourceForge.net(http://nmap4j.sourceforge.net)上查看Nmap4j。 这是一个简单的API,允许您将Nmap集成到Java应用程序中。

–Jon

我编写了自己的异步portscanner java服务,可以像Nmap一样通过TCP-SYN-Scan扫描端口。 它还支持IMCP ping扫描,并且可以以非常高的吞吐量工作(取决于网络可以支持的内容):

https://github.com/subes/invesdwin-webproxy

在内部,它使用java绑定pcap并通过JMS / AMQP公开其服务。 虽然您也可以直接在应用程序中使用它,但如果您不介意它具有root权限。