【问题标题】:Fastest way to scan ports with Java使用 Java 扫描端口的最快方法
【发布时间】:2012-07-17 19:48:46
【问题描述】:

我做了一个非常简单的端口扫描器,但是它运行得太慢了,所以我正在寻找一种方法让它扫描得更快。这是我的代码:

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?

【问题讨论】:

    标签: java sockets ip port-scanning


    【解决方案1】:

    如果 65536 个端口中的每一个都需要 200 毫秒(在最坏的情况下,防火墙会阻止一切,从而使每个端口都达到超时),那么数学很简单:你需要 13k 秒,或者大约三个半小时。

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

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

    由于操作受 I/O 限制(与 CPU 限制相反——也就是说,您花费时间等待 I/O,而不是等待完成一些庞大的计算),您可以使用很多很多线程。尝试从 20 开始。他们会在其中划分 3 个半小时,因此最长预计时间约为 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&lt;SomethingElse&gt; 的函数,其中SomethingElse 将保存端口和扫描结果,例如:

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

    然后,在第一个 sn-p 中将 Boolean 更改为 ScanResult,并返回 new ScanResult(port, true) 或 new ScanResult(port, false) 而不仅仅是 true 或 false

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

    【讨论】:

    • 我没用过,但是 SocketFactory (docs.oracle.com/javase/7/docs/api/javax/net/SocketFactory.html) 也没有帮助吗?
    • 我真的看不出使用 SocketFactory 与这个问题有什么关系。你愿意解释一下你的想法吗?
    • 我认为 SocketFactory 可以实现为在支持中具有类似 SocketPool 的东西,这可以提高速度。不确定这是否有效.. 这不是建议,只是对您的方法的查询。
    • 在这种情况下,“SocketPool”完全没有意义。 TCP 套接字是不可变的四元组(src-ip, src-port, dest-ip, dest-port)(即,您不能在连接中间更改 ips 或端口);由于您测试的每个端口都是不同的目标端口,因此您会发现您需要为每个测试端口使用不同的 TCP 套接字,因此,没有池可以提供帮助。
    • 第一个代码给了我很多错误,甚至无法编译代码,你确定它的一切都是正确的吗?
    【解决方案2】:

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

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

    【讨论】:

    • 请给我一个完整的sn-p例子。 spoon-worlds-fastest-port-scanner.html 路径不起作用。 stackoverflow.com/questions/66742594/… 我正在尝试实现与您相同的想法,但我没有成功
    【解决方案3】:

    如果您决定使用 Nmap 选项并希望继续使用 Java,您应该查看 Nmap4j on SourceForge.net。

    这是一个简单的 API,可让您将 Nmap 集成到 Java 应用程序中。

    【讨论】:

    【解决方案4】:

    代码示例的灵感来自“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<Future<ScanResult>> 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<ScanResult> 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<ScanResult> portIsOpen(final ExecutorService es, final String ip, final int port,
            final int timeout) {
        return es.submit(new Callable<ScanResult>() {
            @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;
        }
    
    }
    }
    

    【讨论】:

      【解决方案5】:

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

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

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

      【讨论】:

        【解决方案6】:

        不,这里最快的方法是使用动态创建的线程方法

        Executors.newCachedThreadPool();
        

        这样它会使用线程直到所有线程都被占用,然后当所有线程都被占用并且有一个新任务时,它将打开一个新线程并在其上执行新任务。

        这是我的代码 sn-p(感谢 Jack 和 Bruno Reis)

        我还添加了搜索您输入的任何 IP 地址的功能,以获得一些附加功能和易用性。

            import java.net.InetSocketAddress;
            import java.net.Socket;
            import java.util.ArrayList;
            import java.util.List;
            import java.util.Scanner;
            import java.util.concurrent.Callable;
            import java.util.concurrent.ExecutionException;
            import java.util.concurrent.ExecutorService;
            import java.util.concurrent.Executors;
            import java.util.concurrent.Future;
            import java.util.concurrent.TimeUnit;
        
            class PortScanner {
        
            public static void main(final String... args) throws InterruptedException, ExecutionException 
            {
                final ExecutorService es = Executors.newCachedThreadPool();
                System.out.print("Please input the ip address you would like to scan for open ports: ");
                Scanner inputScanner = new Scanner(System.in);
                final String ip = inputScanner.nextLine();
                final int timeout = 200;
                final List<Future<ScanResult>> 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<ScanResult> 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)");
         es.shutdown();
            }
        
        
        
            public static Future<ScanResult> portIsOpen(final ExecutorService es, final String ip, final int port,
            final int timeout) 
            {
                return es.submit(new Callable<ScanResult>() {
                    @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;
        }
        
            }
            }
        

        【讨论】:

          【解决方案7】:

          我可能迟到了,但是您可以通过使用 NIO2 单线程执行以下操作来进行批量端口扫描。通过使用单线程跟踪 NIO2 代码,我能够扫描所有主机以查找给定端口。请尝试合理的超时时间并确保您有较大的文件描述符用于进程

          public static List<HostTarget> getRechabilityStatus(String...hosts,final int port, final int bulkDevicesPingTimeoutinMS) throws Exception {
          
              List<AsynchronousSocketChannel> channels = new ArrayList<>(hosts.length);
              try {
                  List<CompletableFuture<HostTarget>> all = new ArrayList<>(hosts.length);
          
                  List<HostTarget> allHosts = new ArrayList(hosts.length);
                  for (String host : hosts) {
                      InetSocketAddress address = new InetSocketAddress(host, port);
                      HostTarget target = new HostTarget();
                      target.setIpAddress(host);
                      allHosts.add(target);
                      AsynchronousSocketChannel client = AsynchronousSocketChannel.open();
                      channels.add(client);
                      final CompletableFuture<HostTarget> targetFuture = new CompletableFuture<>();
                      all.add(targetFuture);
                      client.connect(address, target, new CompletionHandler<Void, HostTarget>() {
                          @Override
                          public void completed(Void result, HostTarget attachment) {
                              attachment.setIsReachable(true);
                              targetFuture.complete(attachment);
                          }
          
                          @Override
                          public void failed(Throwable exc, HostTarget attachment) {
                              attachment.setIsReachable(false);
                              attachment.errrorMessage = exc.getMessage();
                              targetFuture.complete(attachment);
                          }
                      });
                  }
          
                  try {
                      if(bulkDevicesPingTimeoutinMS > 0) {
                          CompletableFuture.allOf(all.toArray(new CompletableFuture[]{})).get(bulkDevicesPingTimeoutinMS, TimeUnit.MILLISECONDS);
                      }else{
                          // wait for all future to be complete 1000 scan is taking 7 seconds.
                          CompletableFuture.allOf(all.toArray(new CompletableFuture[]{})).join();
                      }
                  } catch (Exception timeoutException) {
                      // ignore
                  }
                  return allHosts;
              }finally {
                  for(AsynchronousSocketChannel channel : channels){
                      try{
                          channel.close();
                      }catch (Exception e){
                          if(LOGGER.isDebugEnabled()) {
                              LOGGER.error("Erorr while closing socket",e);
                          }
                      }
          
                  }
              }
          static class HostTarget {
          
          String ipAddress;
          Boolean isReachable;
           public String getIpAddress() {
              return ipAddress;
          }
          public Boolean getIsReachable() {
              return isReachable;
          }
          
          public void setIpAddress(String ipAddress) {
              this.ipAddress = ipAddress;
          }
          public void setIsReachable(Boolean isReachable) {
              this.isReachable = isReachable;
          }
          
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2014-11-28
            • 2020-11-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-08-31
            • 2018-11-19
            相关资源
            最近更新 更多