使用Java开发多线程端口扫描工具

本文涉及的产品
.cn 域名,1个 12个月
简介:

一 扫描原理

其实原理非常简单,就是使用Socket去连接目标IP或者域名的指定端口,如果能够连上则说明该端口是打开的。反之,要是在连接超时之前都没有连上,则将该端口判断为关闭状态。下面我将分别说明两种基本的扫描方式:(1)扫描一个连续的端口段;(2)仅扫描一个指定的端口集合

二 使用多线程扫描目标主机一个段的端口开放情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
      * 多线程扫描目标主机一个段的端口开放情况
     
      * @param ip
      *            待扫描IP或域名,eg:180.97.161.184 www.zifangsky.cn
      * @param startPort
      *            起始端口
      * @param endPort
      *            结束端口
      * @param threadNumber
      *            线程数
      * @param timeout
      *            连接超时时间
      * */
     public  void  scanLargePorts(String ip,  int  startPort,  int  endPort,
             int  threadNumber,  int  timeout) {
         ExecutorService threadPool = Executors.newCachedThreadPool();
         for  ( int  i =  0 ; i < threadNumber; i++) {
             ScanMethod1 scanMethod1 =  new  ScanMethod1(ip, startPort, endPort,
                     threadNumber, i, timeout);
             threadPool.execute(scanMethod1);
         }
         threadPool.shutdown();
         // 每秒中查看一次是否已经扫描结束
         while  ( true ) {
             if  (threadPool.isTerminated()) {
                 System.out.println( "扫描结束" );
                 break ;
             }
             try  {
                 Thread.sleep( 1000 );
             catch  (InterruptedException e) {
                 e.printStackTrace();
             }
         }
     }

然后是一个内部类ScanMethod1实现了Runnable接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/**
      * 扫描方式一:针对起始结束端口,进行逐个扫描
     
      * */
     class  ScanMethod1  implements  Runnable {
         private  String ip;  // 目标IP
         private  int  startPort, endPort, threadNumber, serial, timeout;  // 起始和结束端口,线程数,这是第几个线程,超时时间
 
         /**
          * 初始化
         
          * @param ip
          *            待扫描IP或域名
          * @param startPort
          *            起始端口
          * @param endPort
          *            结束端口
          * @param threadNumber
          *            线程数
          * @param serial
          *            标记是第几个线程
          * @param timeout
          *            连接超时时间
          * */
         public  ScanMethod1(String ip,  int  startPort,  int  endPort,
                 int  threadNumber,  int  serial,  int  timeout) {
             this .ip = ip;
             this .startPort = startPort;
             this .endPort = endPort;
             this .threadNumber = threadNumber;
             this .serial = serial;
             this .timeout = timeout;
         }
 
         public  void  run() {
             int  port =  0 ;
             try  {
                 InetAddress address = InetAddress.getByName(ip);
                 Socket socket;
                 SocketAddress socketAddress;
                 for  (port = startPort + serial; port <= endPort; port += threadNumber) {
                     socket =  new  Socket();
                     socketAddress =  new  InetSocketAddress(address, port);
                     try  {
                         socket.connect(socketAddress, timeout);  // 超时时间
                         socket.close();
                         System.out.println( "端口 "  + port +  " :开放" );
                     catch  (IOException e) {
                         // System.out.println("端口 " + port + " :关闭");
                     }
                 }
             catch  (UnknownHostException e) {
                 e.printStackTrace();
             }
         }
 
     }

三 使用多线程扫描目标主机指定Set端口集合的开放情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
      * 多线程扫描目标主机指定Set端口集合的开放情况
     
      * @param ip
      *            待扫描IP或域名,eg:180.97.161.184 www.zifangsky.cn
      * @param portSet
      *            待扫描的端口的Set集合
      * @param threadNumber
      *            线程数
      * @param timeout
      *            连接超时时间
      * */
     public  void  scanLargePorts(String ip, Set<Integer> portSet,
             int  threadNumber,  int  timeout) {
         ExecutorService threadPool = Executors.newCachedThreadPool();
         for  ( int  i =  0 ; i < threadNumber; i++) {
             ScanMethod2 scanMethod2 =  new  ScanMethod2(ip, portSet,
                     threadNumber, i, timeout);
             threadPool.execute(scanMethod2);
         }
         threadPool.shutdown();
         while  ( true ) {
             if  (threadPool.isTerminated()) {
                 System.out.println( "扫描结束" );
                 break ;
             }
             try  {
                 Thread.sleep( 1000 );
             catch  (InterruptedException e) {
                 e.printStackTrace();
             }
         }
     }

具体的线程内部类跟上面类似,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
      * 扫描方式二:针对一个待扫描的端口的Set集合进行扫描
     
      * */
     private  class  ScanMethod2  implements  Runnable {
         private  String ip;  // 目标IP
         private  Set<Integer> portSet;  // 待扫描的端口的Set集合
         private  int  threadNumber, serial, timeout;  // 线程数,这是第几个线程,超时时间
 
         public  ScanMethod2(String ip, Set<Integer> portSet,  int  threadNumber,
                 int  serial,  int  timeout) {
             this .ip = ip;
             this .portSet = portSet;
             this .threadNumber = threadNumber;
             this .serial = serial;
             this .timeout = timeout;
         }
 
         public  void  run() {
             int  port =  0 ;
             Integer[] ports = portSet.toArray( new  Integer[portSet.size()]);  // Set转数组
             try  {
                 InetAddress address = InetAddress.getByName(ip);
                 Socket socket;
                 SocketAddress socketAddress;
                 if  (ports.length <  1 )
                     return ;
                 for  (port =  0  + serial; port <= ports.length -  1 ; port += threadNumber) {
                     socket =  new  Socket();
                     socketAddress =  new  InetSocketAddress(address, ports[port]);
                     try  {
                         socket.connect(socketAddress, timeout);
                         socket.close();
                         System.out.println( "端口 "  + ports[port] +  " :开放" );
                     catch  (IOException e) {
                         // System.out.println("端口 " + ports[port] + " :关闭");
                     }
                 }
             catch  (UnknownHostException e) {
                 e.printStackTrace();
             }
 
         }
 
     }

四 两种扫描方式的测试用例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public  static  void  main(String[] args) {
         PortScanDemo portScanDemo =  new  PortScanDemo();
         //方式1
         // portScanDemo.scanLargePorts("ultra-book.co", 20, 10000, 5,800);
         // portScanDemo.scanLargePorts("180.97.161.184", 1, 100, 5);
 
         //方式2
         Set<Integer> portSet =  new  LinkedHashSet<Integer>();
         Integer[] ports =  new  Integer[] {  21 22 23 25 26 69 80 110 143 ,
                 443 465 995 1080 1158 1433 1521 2100 3128 3306 3389 ,
                 7001 8080 8081 9080 9090 , 43958 };
         portSet.addAll(Arrays.asList(ports));
         portScanDemo.scanLargePorts( "ultra-book.co" , portSet,  5 800 );
 
     }

五 测试结果

wKioL1Z4oyXB_3cYAAFDqJgRSFQ285.png

wKioL1Z4oyWxTyktAABNvxk7sL4968.png

注:1 超时时间是以毫秒为单位,其中要是扫描国内的IP可以把这个时间适当设置低一点,200~500左右。相反,要是扫描国外IP就需要把这个时间适当设置大一点,不然有可能把本来打开的端口也漏掉了



本文转自 pangfc 51CTO博客,原文链接:http://blog.51cto.com/983836259/1727023,如需转载请自行联系原作者

相关文章
|
6天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
37 6
|
15天前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####
|
15天前
|
Java 调度
Java中的多线程编程与并发控制
本文深入探讨了Java编程语言中多线程编程的基础知识和并发控制机制。文章首先介绍了多线程的基本概念,包括线程的定义、生命周期以及在Java中创建和管理线程的方法。接着,详细讲解了Java提供的同步机制,如synchronized关键字、wait()和notify()方法等,以及如何通过这些机制实现线程间的协调与通信。最后,本文还讨论了一些常见的并发问题,例如死锁、竞态条件等,并提供了相应的解决策略。
40 3
|
16天前
|
监控 Java 开发者
深入理解Java中的线程池实现原理及其性能优化####
本文旨在揭示Java中线程池的核心工作机制,通过剖析其背后的设计思想与实现细节,为读者提供一份详尽的线程池性能优化指南。不同于传统的技术教程,本文将采用一种互动式探索的方式,带领大家从理论到实践,逐步揭开线程池高效管理线程资源的奥秘。无论你是Java并发编程的初学者,还是寻求性能调优技巧的资深开发者,都能在本文中找到有价值的内容。 ####
|
19天前
|
监控 Java 数据库连接
Java线程管理:守护线程与用户线程的区分与应用
在Java多线程编程中,线程可以分为守护线程(Daemon Thread)和用户线程(User Thread)。这两种线程在行为和用途上有着明显的区别,了解它们的差异对于编写高效、稳定的并发程序至关重要。
28 2
|
5月前
|
弹性计算 应用服务中间件 Linux
阿里云服务器开放端口完整图文教程
笔者近期开发完成的服务端程序部署在阿里云的ECS云服务器上面,一些应用程序配置文件需要设置监听的端口(如Tomcat的8080、443端口等),虽然通过CentOs 7系统的的「防火墙」开放了对应的端口号,任然无法访问端口号对应的应用程序,后面了解到原来还需要设置云服务器的「安全组规则」,开放相应的端口权限,服务端的接口才能真正开放。
739 1
阿里云服务器开放端口完整图文教程
|
5月前
|
弹性计算 运维 数据安全/隐私保护
云服务器 ECS产品使用问题之如何更改服务器的IP地址或端口号
云服务器ECS(Elastic Compute Service)是各大云服务商阿里云提供的一种基础云计算服务,它允许用户租用云端计算资源来部署和运行各种应用程序。以下是一个关于如何使用ECS产品的综合指南。
|
4月前
|
缓存 NoSQL 网络安全
【Azure Redis 缓存】使用开源工具redis-copy时遇见6379端口无法连接到Redis服务器的问题
【Azure Redis 缓存】使用开源工具redis-copy时遇见6379端口无法连接到Redis服务器的问题
|
5月前
|
网络协议 Linux Unix
面试官:服务器最大可以创建多少个tcp连接以及端口并解释下你对文件句柄的理解
面试官:服务器最大可以创建多少个tcp连接以及端口并解释下你对文件句柄的理解
149 0
面试官:服务器最大可以创建多少个tcp连接以及端口并解释下你对文件句柄的理解
|
4月前
|
网络协议
【qt】TCP的监听 (设置服务器IP地址和端口号)
【qt】TCP的监听 (设置服务器IP地址和端口号)
256 0
下一篇
DataWorks