Java开发技术:Socket编程端口扫描小程序(完整源码)
Posted Charzous
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java开发技术:Socket编程端口扫描小程序(完整源码)相关的知识,希望对你有一定的参考价值。
目录
一、端口扫描技术
我们都知道,网络中的每台机器都有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
以上是关于Java开发技术:Socket编程端口扫描小程序(完整源码)的主要内容,如果未能解决你的问题,请参考以下文章
今天用Java开发主机IP扫描神器,零基础Socket编程详细