Java开发技术:Socket编程端口扫描小程序(完整源码)

Socket网络编程 专栏收录该内容
11 篇文章 6 订阅

 目录

一、端口扫描技术

二、 单线程建立 Socket 连接

三、 基于 TCP Connect 端口扫描

四、 多线程 TCP 连接扫描

五、 小程序完整源码


一、端口扫描技术

我们都知道,网络中的每台机器都有IP地址,与IP地址密切相关的就是主机的端口,顺便考考你,知道端口号的范围吗?

没错,就是0~65535。在计算机网络中,使用两个字节供16位二进制数来表示端口号。对于这些端口号,一台主机它有开放哪些端口号呢?

比如常见的80,443两个端口号对应的就是HTTP,HTTPS服务23 端口对应telnet远程管理25 端口是SMTP服务等等。下面我们就来实现一个简易的端口扫描小程序。

扫描主机开放的端口是常见操作:攻(寻找目的主机开放的端口)与防(检测本机异常开放的端口)。基本的端口扫描可使用的技术:

  • 创建Socket连接:new Socket(ip,port)
  • TCP Connect 探测

本篇适用计算机网络、Java开发、Socket编程以及多线程入门的伙伴!可以结合上一篇《Java开发主机IP扫描神器》,进一步探测网络攻防有用信息!

二、 单线程建立 Socket 连接

现在开始小程序的简易开发流程,从单线程开始一步一步前进。

Socket socket=new Socket(host,port);

这里,我们通过创建一个Socket连接,这种方式原理就是与目标IP建立连接,监听端口,如果超过一定时间,说明该端口没有开放,无法成功连接。

无法连接的情况会抛出 IOException 异常,因此,可以在抛出异常的时候判定端口是关闭状态。

具体地实现很简单,我们把扫描端口的操作放到一个线程里面,不影响主程序的运行,然后对给出的端口范围比如 0到100,逐个进行Socket连接,这样就可以知道哪些端口的open的。


try {
     Socket socket=new Socket(host,port);
     socket.close();
     Platform.runLater(() -> {
          result.appendText("端口 " +port+ " is open.\n");
     });
} catch (IOException e) {
   result.appendText("端口 " +port+ " is closed.\n");
}

 

这是对我的主机部分端口扫描的结果。

下面是动图可以看一下过程。

三、 基于 TCP Connect 端口扫描

从动态图的扫描过程可以发现,这个速度明显不是人可以容忍的,就像一个网页加载半天一直没显示出来一样,所以,为了更好的用户体验,在此基础上进行优化改进,最重要的就是扫描速度!

上一个方法在遇到端口关闭时等待时间过长,时间成本过高。开始使用另一个方法,基于TCP Connect 的端口扫描。

Socket socket=new Socket();
socket.connect(new InetSocketAddress(host,port),200);
socket.close();

 这时候,不会对每个ip和端口真正建立连接,而是使用“探测”的方式,通过InetSocketAddress类进行连接,超时时间设定200ms,明显这样速度至少可以提高几倍!

代码实现很简单,这样写就搞定啦

try {
    Socket socket=new Socket();
    socket.connect(new InetSocketAddress(host,port),200);

    socket.close();
    Platform.runLater(() -> {
        result.appendText("端口 " +port+ " is open.\n");
    });
} catch (IOException e) {
    result.appendText("端口 " +port+ " is closed.\n");
}

 优化改进之后的对比扫描速度如下动图:

对比之下,扫描速度大幅度提升!

这时,速度的提升是相对而言,比如扫描1000个端口,你就知道有多慢了,还是等不及!

20秒竟然才扫描不到80个端口!粗略计算,1000个端口至少需要4分钟,这是无法接受的!

四、 多线程 TCP 连接扫描

这时候,只能使用后手了,多线程专场…… 嘿嘿

我们现在的小目标就是速度要快,多线程操作真不赖。这里我开启100个线程,来完成这项扫描的任务。

for (int i=0;i<100;i++) {
    readThread=new Thread(new ScanHandler(i, 100),"scanThread");
    readThread.start();
}

 同时,开启多线程就需要注意线程安全问题,在这个任务,还需要关注每个线程负责的端口范围,避免重复扫描以及扫描结束判断。

使用原子变量来保证线程安全,计数正确解决上述问题。

static AtomicInteger portCount portCount=new AtomicInteger(0);

 定义一个扫描线程的处理类ScanHandler,处理逻辑大概就是:每个线程有自己的标识号,根据标识号确定自己负责的端口号,每个线程都维护着已扫描的端口原子计数

    class ScanHandler implements Runnable{
        private int totalThreadNum;//用于端口扫描的总共线程数量,默认为10
        private int threadNo;//线程号,表示第几个线程
        private int startP=Integer.parseInt(startPort.getText());
        private int endP=Integer.parseInt(endPort.getText());
        private String host = targetIP.getText().trim();

        public ScanHandler(int threadNo) {
            this.totalThreadNum = 10;
            this.threadNo = threadNo;
        }

        public ScanHandler(int threadNo,int totalThreadNum) {
            this.totalThreadNum = totalThreadNum;
            this.threadNo = threadNo;
        }

        @Override
        public void run() {
            //startPort和endPort为成员变量,表示需要扫描的起止端口
            for (int i=startP+threadNo;i<=endP;i=i+totalThreadNum){
                int port=i;
                if (readThread.isInterrupted()){
                    readThread.interrupt();
                    break;
                }
                try {
                    Socket socket=new Socket();
                    socket.connect(new InetSocketAddress(host,port),200);
                    socket.close();
                    Platform.runLater(() -> {
                        result.appendText("端口 " +port+ " is open.\n");
                    });
                }catch (IOException e){
//                    result.appendText("端口 " +i+ " is closed.\n");
                }
                portCount.incrementAndGet();

            }
            if (portCount.get()==(endP-startP+1)){//判断扫描结束
                portCount.incrementAndGet();
                Platform.runLater(()->{
                    result.appendText("\n-------------多线程扫描结束-------------\n");
                });
            }
        }
    }

 再来看看多线程操作的速度有多快,现在感觉很顺畅,1000个端口不到10秒就完成扫描!

是不是感觉挺完美了,但根据常识总是差点什么,其实就是一个进度条显示,就像我们下载东西一样,有个进度条和百分比看着心里有底,安排!

Java中就有可以直接使用的进度类,就是下面这行代码,然后再加到扫描线程里面,实时更新进度。

ProgressBar progressBar=new ProgressBar();

 来看看效果,用多线程版本扫描5000个端口。

五、 小程序完整源码

完整源码,毫无保留,建议果断收藏,以免以后使用找不到!

/**
 * HostScannerFX.java
 * Copyright (c) 2021 Charzous
 * All right reserved.
 * @date 2021-06-07 下午 09:38
 */


import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;


import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.concurrent.atomic.AtomicInteger;

public class PortScannerFX extends Application {
    private TextArea result = new TextArea();
    private TextField targetIP = new TextField();
    private TextField startPort = new TextField();
    private TextField endPort = new TextField();
    private Button scan = new Button("扫描");
    private Button quickScan = new Button("快速扫描");
    private Button threadScan = new Button("多线程扫描");
    private Button ex=new Button("退出");
    private Button stop=new Button("停止扫描");
    private Thread readThread;
    static AtomicInteger portCount;//用于统计已扫描的端口数量

    private ProgressBar progressBar=new ProgressBar();
    private Label bar=new Label("0%");

    @Override
    public void start(Stage primaryStage) throws Exception {
        BorderPane mainPane = new BorderPane();

        HBox barBox=new HBox();
        barBox.setSpacing(10);
        barBox.setPadding(new Insets(10, 0, 10, 0));
        progressBar.setPrefWidth(700);
        progressBar.setProgress(0);
        HBox.setHgrow(progressBar,Priority.ALWAYS);
        barBox.getChildren().addAll(bar,progressBar);

        VBox vBox = new VBox();
        vBox.setSpacing(10);
        vBox.setPadding(new Insets(10, 20, 10, 20));
//        vBox.setAlignment(Pos.CENTER);
        VBox.setVgrow(result, Priority.ALWAYS);
        vBox.getChildren().addAll(new Label("端口扫描结果:"), result,barBox);
        mainPane.setCenter(vBox);

        startPort.setPrefWidth(60);
        endPort.setPrefWidth(60);
        HBox hBox1 = new HBox();
        hBox1.setSpacing(10);
        hBox1.setPadding(new Insets(10, 20, 10, 20));
        hBox1.setAlignment(Pos.CENTER);
        hBox1.getChildren().addAll(new Label("目标主机ip:"), targetIP, new Label("起始端口号:"), startPort, new Label("结束端口号:"),endPort);

        HBox hBox2 = new HBox();
        hBox2.setSpacing(10);
        hBox2.setPadding(new Insets(10, 20, 10, 20));
        hBox2.setAlignment(Pos.CENTER);

        hBox2.getChildren().addAll(scan,quickScan,threadScan,stop,ex);

        VBox vBox1 = new VBox();
        vBox1.setSpacing(10);
        vBox1.setPadding(new Insets(10, 20, 10, 20));
        vBox1.setAlignment(Pos.CENTER);
        vBox1.getChildren().addAll(hBox1, hBox2);
        mainPane.setBottom(vBox1);

        Scene scene = new Scene(mainPane, 800, 500);
        primaryStage.setScene(scene);
        primaryStage.setTitle("PortScannerFX");
        primaryStage.show();



        //扫描
        scan.setOnAction(event -> {
            String host = targetIP.getText().trim();
            int sp = Integer.parseInt(startPort.getText());
            int ep=Integer.parseInt(endPort.getText());
            readThread = new Thread(() -> {

                double num=0.0;
                for (int i = sp; i <= ep; i++) {
                    int port=i;
                    if (readThread.isInterrupted()){
                        readThread.interrupt();
//                        Thread.interrupted();
                        break;
                    }
                    try {
                        Socket socket=new Socket(host,port);
                        socket.close();
                        Platform.runLater(() -> {
                                result.appendText("端口 " +port+ " is open.\n");
                        });
                    } catch (IOException e) {
                        result.appendText("端口 " +port+ " is closed.\n");
                    }
                    num++;
                    double finalNum = num;
                    Platform.runLater(()->{
                        progressBar.setProgress(finalNum/(ep-sp+1));//进度条
                        bar.setText(""+ Integer.valueOf((int) (finalNum /(ep-sp+1)*100))+"%");
                    });
                }
                result.appendText("端口扫描结束!\n");
            },"scanThread");
            readThread.start();

        });

        //快速扫描
        quickScan.setOnAction(event -> {
            String host = targetIP.getText().trim();
            int sp = Integer.parseInt(startPort.getText());
            int ep=Integer.parseInt(endPort.getText());
            readThread = new Thread(() -> {

                double num=0;
                for (int i = sp; i <= ep; i++) {
                    if (readThread.isInterrupted()){
                        readThread.interrupt();
//                        Thread.interrupted();
                        break;
                    }
                    int port=i;
                    try {
                        Socket socket=new Socket();
                        socket.connect(new InetSocketAddress(host,port),200);

                        socket.close();
                        Platform.runLater(() -> {
                            result.appendText("端口 " +port+ " is open.\n");
                        });
                    } catch (IOException e) {
                        result.appendText("端口 " +port+ " is closed.\n");
                    }
                    num++;
                    double finalNum = num;
                    Platform.runLater(()->{
                        progressBar.setProgress(finalNum/(ep-sp+1));//进度条
                        bar.setText(""+ Integer.valueOf((int) (finalNum /(ep-sp+1)*100))+"%");
                    });
                }
                result.appendText("端口扫描结束!\n");
            },"scanThread");
            readThread.start();
        });

        threadScan.setOnAction(event -> {
            portCount=new AtomicInteger(0);
            int sp = Integer.parseInt(startPort.getText());
            int ep=Integer.parseInt(endPort.getText());
            for (int i=0;i<100;i++) {
                readThread=new Thread(new ScanHandler(i, 100),"scanThread");
                readThread.start();
            }
        });

        stop.setOnAction(event -> {
            interrupt("scanThread");
        });

        //退出
        ex.setOnAction(event -> {
            exit();
        });
        primaryStage.setOnCloseRequest(event -> {
            exit();
        });
    }

    public void interrupt(String threadName){
        ThreadGroup currentGroup=Thread.currentThread().getThreadGroup();
        int noThreads=currentGroup.activeCount();
        Thread[] lstThreads=new Thread[noThreads];
        currentGroup.enumerate(lstThreads);
        for (int i=0;i<noThreads;i++){
            if (lstThreads[i].getName().equals(threadName))
                lstThreads[i].interrupt();
        }
    }

    public void exit(){
        System.exit(0);
    }



    class ScanHandler implements Runnable{
        private int totalThreadNum;//用于端口扫描的总共线程数量,默认为10
        private int threadNo;//线程号,表示第几个线程
        private int startP=Integer.parseInt(startPort.getText());
        private int endP=Integer.parseInt(endPort.getText());
        private String host = targetIP.getText().trim();

        public ScanHandler(int threadNo) {
            this.totalThreadNum = 10;
            this.threadNo = threadNo;
        }

        public ScanHandler(int threadNo,int totalThreadNum) {
            this.totalThreadNum = totalThreadNum;
            this.threadNo = threadNo;
        }

        @Override
        public void run() {
            //startPort和endPort为成员变量,表示需要扫描的起止端口
            for (int i=startP+threadNo;i<=endP;i=i+totalThreadNum){
                int port=i;
                if (readThread.isInterrupted()){
                    readThread.interrupt();
                    break;
                }
                try {
                    Socket socket=new Socket();
                    socket.connect(new InetSocketAddress(host,port),200);
                    socket.close();
                    Platform.runLater(() -> {
                        result.appendText("端口 " +port+ " is open.\n");
                    });
                }catch (IOException e){
//                    result.appendText("端口 " +i+ " is closed.\n");
                }
                portCount.incrementAndGet();
                Platform.runLater(()->{
                    bar.setText(""+ Integer.valueOf((int) ((portCount.doubleValue())/(endP-startP+1)*100))+"%");//进度比
                    progressBar.setProgress((portCount.doubleValue())/(endP-startP+1));//进度条
                });
            }
            if (portCount.get()==(endP-startP+1)){//判断扫描结束
                portCount.incrementAndGet();
                Platform.runLater(()->{
                    result.appendText("\n-------------多线程扫描结束-------------\n");
                });
            }
        }
    }


    public static void main(String[] args) {
        launch();
    }
}


完整源码,毫无保留,建议果断收藏,以免以后使用找不到,赶紧自己动手开发一个简易端口扫描小程序!

 

今天用Java开发技术:Socket编程端口扫描小程序,零基础Socket编程详细教程,这篇内容是不是简单、有趣、有收获呢?欢迎交流学习!

最后想跟大家说的是,学习Java必备的知识有哪些呢?很多粉丝私信我Java学习的路线,我推荐了这套知识图谱,粉丝们都觉得质量很不错!

学习Java开发,Socket网络编程等知识,里面有许多有趣的小程序可以做,最近我也在跟着这一套 《Java 工程师学习成长知识图谱》进行体系的学习,是CSDN官方推出的,质量很不错!

其中包含了Java专业体系结构完整详细,推荐给大家学习使用,有兴趣可以扫码查看,最近我也在学习当中,当然,我的文章会记录学习,欢迎大家阅读,比如我的专栏《Socket网络编程》、《Java宝藏》

展开就是这样的,尺寸870mm*560mm排版好看,内容很充实。推荐给有需要的伙伴,一起来学习Java开发!


如果觉得不错欢迎“一键三连”哦,点赞收藏关注,评论提问建议,欢迎交流学习!一起加油进步,我们下篇见! 

本篇内容首发我的CSDN博客:https://csdn-czh.blog.csdn.net/article/details/117672622

Applet钢琴模拟程序java源码 2个目标文件,提供基本的音乐编辑功能。编辑音乐软件的朋友,这款实例会对你有所帮助。 Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 6个目标文件,EJB来模拟银行ATM机的流程及操作获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用完毕,从内存中清除,从账户中取出amt,如果amt>账户余额抛出异常,一个实体Bean可以表示不同的数据实例,我们应该通过主键来判断删除哪个数据实例…… ejbCreate函数用于初始化一个EJB实例 5个目标文件,演示Address EJB的实现,创建一个EJB测试客户端,得到名字上下文,查询jndi名,通过强制转型得到Home接口,getInitialContext()函数返回一个经过初始化的上下文,用client的getHome()函数调用Home接口函数得到远程接口的引用,用远程接口的引用访问EJB。 EJB中JNDI的使用源码例子 1个目标文件,JNDI的使用例子,有源代码,可以下载参考,JNDI的使用,初始化Context,它是连接JNDI树的起始点,查找你要的对象,打印找到的对象,关闭Context…… ftp文件传输 2个目标文件,FTP的目标是1提高文件的共享性计算机程序和/或数据2鼓励间接地通过程序使用远程计算机,3保护用户因主机之间的文件存储系统导致的变化,4为了可靠和高效地传输,虽然用户可以在终端上直接地使用它,但是它的主要作用是供程序使用的。本规范尝试满足大型主机、微型主机、个人工作站、和TACs 的不同需求。例如,容易实现协议的设计。 Java EJB中有、无状态SessionBean的两个例子 两个例子,无状态SessionBean可会话Bean必须实现SessionBean,获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,计算利息等;在有状态SessionBean中,用累加器,以对话状态存储起来,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用完毕,从内存中清除…… Java Socket 聊天通信演示代码 2个目标文件,一个服务器,一个客户端。 Java Telnet客户端实例源码 一个目标文件,演示Socket的使用。 Java 组播组中发送和接受数据实例 3个目标文件。 Java读写文本文件的示例代码 1个目标文件。 java俄罗斯方块 一个目标文件。 Java非对称加密源码实例 1个目标文件 摘要:Java源码,算法相关,非对称加密   Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。   设定字符串为“张三,你好,我是李四”   产生张三的密钥对(keyPairZhang)   张三生成公钥(publicKeyZhang)并发送给李四,这里发送的是公钥的数组字节   通过网络或磁盘等方式,把公钥编码传送给李四,李四接收到张三编码后的公钥,将其解码,李四用张三的公钥加密信息,并发送给李四,张三用自己的私钥解密从李四处收到的信息…… Java利用DES私钥对称加密代码实例 同上 java聊天室 2个目标文件,简单。 java模拟掷骰子2个 1个目标文件,输出演示。 java凭图游戏 一个目标文件,简单。 java求一个整数的因子 如题。 Java生成密钥的实例 1个目标文件 摘要:Java源码,算法相关,密钥   Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥,通常应对私钥加密后再保存、如何从文件中得到公钥编码的字节数组、如何从字节数组解码公钥。 Java数据压缩与传输实例 1个目标文件 摘要:Java源码,文件操作,数据压缩,文件传输   Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲区、写入数据到文件、关闭输入流、关闭套接字关闭输出流、输出错误信息等Java编程小技巧。 Java数组倒置 简单 Java图片加水印,支持旋转和透明度设置 摘要:Java源码,文件操作,图片水印   util实现Java图片水印添加功能,有添加图片水印和文字水印,可以设置水印位置,透明度、设
相关推荐
第1篇 Java编程基础   第1章 Java开发环境的搭建教学视频9分钟 2   1.1 理解Java 2   1.2 搭建Java所需环境 3   1.2.1 下载JDK 3   1.2.2 安装JDK 4   1.2.3 配置环境 5   1.2.4 测试JDK配置是否成功 7   实例1 开发第一个Java程序 7   第2章 Java基础类型与运算符教学视频39分钟 9   2.1 基础类型 9   实例2 自动提升 9   实例3 自动转换 10   实例4 常用基础类型之强制转换 11   2.2 运算符 12   实例5 算术运算符 12   实例6 关系运算符 13   实例7 逻辑运算符 14   实例8 位运算符 15   实例9 移位运算符 16   实例10 转型运算符 17   2.3 其他形式 18   实例11 常量与变量 18   实例12 各种进制的转换 19   实例13 Java中的进制与移位运算符 22   第3章 条件控制语句教学视频75分钟 26   3.1 if控制语句 26   实例14 判断输入的年份是否为闰年 26   实例15 抽奖活动 27   3.2 for语句 28   实例16 小九九乘法表 28   实例17 如何列出素数 29   实例18 Java中的递归 31   实例19 男生女生各多少人 32   实例20 求水仙花数 34   实例21 求任意一个正数的阶乘 35   实例22 求n的n次方 35   实例23 利用for循环输出几何图形 36   实例24 杨辉三角 38   3.3 while语句 39   实例25 求1到100之间的和 39   实例26 存上100元需要多少天 40   实例27 输出100之间的所有偶数 41   实例28 如何判断回文数字 42   3.4 do…while语句 43   实例29 输出100之间的所有奇数 44   实例30 求最大的随机数 44   3.5 switch语句 45   实例31 判断字母分类 46   实例32 优良及差 47   实例33 打印任意一年日历 48   实例34 一年四季的划分 51   第2篇 Java数据处理   第4章 异常处理教学视频62分钟 54   4.1 编译时异常 54   实例35 除0发生的算术异常ArithmeticException 54   实例36 数组下标越界异常ArrayIndexOutOfBoundsException 55   实例37 数组元素类型不匹配异常ArrayStoreException 56   实例38 强制类型转换异常ClassCastException 56   实例39 索引越界异常IndexOutOfBoundsException 57   实例40 空指针异常NullPointerException 58   实例41 数字格式转换异常NumberFornatException 59   实例42 字符串索引越界异常StringIndexOutBounds 60   实例43 操作错误UnsupportedOperationException 60   4.2 运行时异常 61   实例44 找不到指定类时发生的异常ClassNotFoundException 62   实例45 请求的方法不存在NoSuchMethodException 63   4.3 try…catch捕获异常 65   实例46 try…catch捕获异常的实例 66   实例47 try…catch…finally捕获异常的实例 67   实例48 try…catch嵌套捕获异常的实例 68   4.4 throws声明异常 69   实例49 throws声明异常实例一 69   实例50 throws声明异常实例二 70   4.5 throw抛出异常 72   实例51 throw抛出异常实例一 72   实例52 throw抛出异常实例二 73   4.6 自定义异常 74   实例53 自定义异常实例一 74   实例54 自定义异常实例二 75   第5章 数组教学视频98分钟 78   5.1 一维数组 78   实例55 一维数组的创建与使用 78   实例56 按相反的顺序输出 79   实例57 奇偶分组 80   实例58 找宝 81   实例59 寻找最小数 82  
©️2020 CSDN 皮肤主题: 博客之星2020 设计师:CY__ 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值