当前位置: 首页 > article >正文

QT给端口扫描工程增加线程2

 前面的步骤:用QT实现 端口扫描工具1-CSDN博客

框架分析

mainwindow.cpp中

有类的构造函数、析构函数,点击事件处理函数,接受信号的槽函数

扫描函数的点击事件中

MyThread新增加了线程mthread,并为其连接信号和槽函数(信号和槽是相对应的,所以要把它们连接起来)

MyThread中

的.h文件中声明函数,MyThread的.cpp文件中进行函数定义

--------------------------------------------------------------------------------------------------------------------------------

加一个button

给Stop和Scan加clicked槽。

增加线程步骤

右键,添加

mythread.h文件

#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QTcpSocket>
#include <QDebug>
class MyThread : public QThread
{
    Q_OBJECT
public:
    MyThread(QString, int);
    void closeThread(); //用于结束线程
signals:
    void send_scan_signal(int, bool); //发送线程扫描结果
protected:
    virtual void run();
private:
    volatile bool isStop; // 线程是否结束,volatile 表示不优化此变量
    QString m_strIP; //属性:目标 IP
    int m_intPort; //属性:目标端口
};

#endif // MYTHREAD_H

mythread.cpp

#include "mythread.h"

MyThread::MyThread(QString strIP, int intPort)
{
    isStop = false; //初始化
    m_strIP = strIP; //给属性赋值
    m_intPort = intPort; //给属性赋值
}
void MyThread::closeThread()
{
    isStop = true; //给属性赋值,true 表示结束线程
}
void MyThread::run()
{
    QTcpSocket socket(0);
    for ( int i = m_intPort; i<=1024; i++ )//从 i 开始扫描,最大扫描到 1024
    {
        if(isStop)
            return;
        socket.abort();
        socket.connectToHost(m_strIP, i);
        if(socket.waitForConnected(1000))
        {
            emit (send_scan_signal(i, true)); // 发送信号
            qDebug("%d: %s",i,"opened"); // 调试信息
        }
        else
        {
            emit (send_scan_signal(i, false));
            qDebug("%d: %s",i,"closed");
        }
        msleep(100);
    }
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
//添加头文件的引用
#include <QMessageBox>
#include <QTreeWidget>
#include "mythread.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
 Q_OBJECT
public:
 MainWindow(QWidget *parent = nullptr);
 ~MainWindow();
private slots:
 void on_pushButton_Scan_clicked();
 void on_stopButton_clicked();
 void on_pushButton_Quit_clicked();
 void recv_result(int, bool); //自定义槽函数,用于接收线程发送的消息;
private:
 Ui::MainWindow *ui;
 QTreeWidgetItem *itemRoot; //树形控件的条目,用于表示目标 IP
 QTreeWidgetItem *itemLeaf; //树形控件的条目,用于表示目标端口
 MyThread *myThread = 0; //线程类
};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"  
#include "ui_mainwindow.h"  

// MainWindow 类的构造函数  执行清理操作
MainWindow::MainWindow(QWidget *parent)  
    : QMainWindow(parent) // 调用基类 QMainWindow 的构造函数  
    , ui(new Ui::MainWindow) // 创建 UI 对象  
{  
    ui->setupUi(this); // 设置用户界面  
    // 给 QTreeWidget 初始化表头  
    QStringList head; // 创建一个字符串列表用于存储表头  
    head << "扫描结果"; // 添加表头项  
    ui->treeWidget->setHeaderLabels(head); // 设置 QTreeWidget 的表头  
}  

// MainWindow 类的析构函数  
MainWindow::~MainWindow()  
{  
    delete ui; // 删除 UI 对象以释放内存  
}  

// 扫描按钮点击事件处理函数  
void MainWindow::on_pushButton_Scan_clicked()  
{  
    ui->pushButton_Scan->setEnabled(false); // 正在扫描时,禁用扫描按钮  
    QString strIP = ui->lineEdit_IP->text(); // 获取用户输入的 IP 地址  
    int intPort = ui->spinBox_Port->value(); // 获取用户输入的端口号  

    // 验证 IP 地址是否为空  
    if(strIP.isEmpty()){  
        QMessageBox::information(this, "Error", "请输入 IP", QMessageBox::Ok); // 显示错误信息  
        return; // 退出函数  
    }  

    // 验证端口号是否为 0  
    if(intPort == 0){  
        QMessageBox::information(this, "Error", "请输入 port", QMessageBox::Ok); // 显示错误信息  
        return; // 退出函数  
    }  

    ui->treeWidget->clear(); // 清空 QTreeWidget 中的先前结果  
    itemRoot = new QTreeWidgetItem(ui->treeWidget, QStringList(strIP)); // 创建根节点,显示 IP 地址  
    myThread = new MyThread(strIP, intPort); // 创建新的扫描线程,传入 IP 和端口  

    // 连接 myThread 的信号 send_scan_signal(int, bool) 和槽函数 recv_result(int, bool)  
    connect(myThread, SIGNAL(send_scan_signal(int, bool)), this, SLOT(recv_result(int, bool))); // 连接信号和槽  
    myThread->start(); // 启动扫描线程  
}  

// 接收扫描结果的槽函数  
void MainWindow::recv_result(int port, bool isOpen)  
{  
    QString strPort = QString::number(port); // 将端口号转换为字符串  
    // 根据端口状态创建叶子节点  
    if(isOpen)  
        itemLeaf = new QTreeWidgetItem(itemRoot, QStringList(strPort + " opened")); // 端口打开  
    else  
        itemLeaf = new QTreeWidgetItem(itemRoot, QStringList(strPort + " closed")); // 端口关闭  
}  

// 停止按钮点击事件处理函数  
void MainWindow::on_stopButton_clicked()  
{  
    // 检查是否有正在运行的线程  
    if(myThread == 0)  
    {  
        QMessageBox::information(this, "Error", "还没有开始", QMessageBox::Ok); // 显示错误信息  
        return; // 退出函数  
    }  

    myThread->closeThread(); // 调用线程类的自定义成员函数以关闭线程  
    myThread->wait(); // 等待线程结束  
    delete myThread; // 删除线程对象以释放内存  
    myThread = 0; // 将线程指针置为 nullptr  
    ui->pushButton_Scan->setEnabled(true); // 重新启用扫描按钮  
}  

// 退出按钮点击事件处理函数  
void MainWindow::on_pushButton_Quit_clicked()  
{  
    QApplication::quit(); // 退出应用程序  
}

在扫描线程结束后在主窗口得到结束信号并将 Scan 按钮设置为可用

在 MyThread 类中定义一个信号,例如 finished(),并在扫描线程完成时发出该信号。然后,在 MainWindow 类中连接这个信号到一个槽函数,该槽函数将重新启用“Scan”按钮。

### 步骤

1.  在 MyThread 类的头文件中,添加一个结束信号:

#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QTcpSocket>
#include <QDebug>
class MyThread : public QThread
{
    Q_OBJECT
public:
    MyThread(QString, int);
    void closeThread(); //用于结束线程
signals:
    void send_scan_signal(int, bool); //发送线程扫描结果
    void finished();
protected:
    virtual void run();
private:
    volatile bool isStop; // 线程是否结束,volatile 表示不优化此变量
    QString m_strIP; //属性:目标 IP
    int m_intPort; //属性:目标端口
};

#endif // MYTHREAD_H

2. 在 `MyThread` 的 `run()` 方法中发射信号

   在扫描完成后,调用 `emit finished();` 来发射这个信号。

MyThread.cpp

#include "MyThread.h"

MyThread::MyThread(QString strIP, int intPort)
{
    isStop = false; //初始化
    m_strIP = strIP; //给属性赋值
    m_intPort = intPort; //给属性赋值
}
void MyThread::closeThread()
{
    isStop = true; //给属性赋值,true 表示结束线程
}
void MyThread::run()
{
    QTcpSocket socket(0);
    for ( int i = m_intPort; i<=1024; i++ )//从 i 开始扫描,最大扫描到 1024
    {
        if(isStop)
            return;
        socket.abort();
        socket.connectToHost(m_strIP, i);
        if(socket.waitForConnected(1000))
        {
            emit (send_scan_signal(i, true)); // 发送信号
            qDebug("%d: %s",i,"opened"); // 调试信息
        }
        else
        {
            emit (send_scan_signal(i, false));
            qDebug("%d: %s",i,"closed");
        }
        msleep(100);
    }
    emit finished(); // 在扫描完成后发出信号  
}

3.在mainwindow的.h文件中声明槽函数

4. 实现槽函数

在 MainWindow 中实现 onScanFinished() 槽函数,重新启用“Scan”按钮:

//接受结束信号的槽函数
void MainWindow::onScanFinished()
{
    ui->pushButton_Scan->setEnabled(true); // 重新启用扫描按钮
}

5. 在 Scan按钮的点击事件`void MainWindow::on_pushButton_Scan_clicked()` 中连接信号和槽:

这样可以确保每次点击“Scan”按钮时,都会正确连接信号和槽。

// 扫描按钮点击事件处理函数
void MainWindow::on_pushButton_Scan_clicked()
{
    ui->pushButton_Scan->setEnabled(false); // 正在扫描时,禁用扫描按钮
    QString strIP = ui->lineEdit_IP->text(); // 获取用户输入的 IP 地址
    int intPort = ui->spinBox_Port->value(); // 获取用户输入的端口号

    // 验证 IP 地址是否为空
    if(strIP.isEmpty()){
        QMessageBox::information(this, "Error", "请输入 IP", QMessageBox::Ok); // 显示错误信息
        return; // 退出函数
    }

    // 验证端口号是否为 0
    if(intPort == 0){
        QMessageBox::information(this, "Error", "请输入 port", QMessageBox::Ok); // 显示错误信息
        return; // 退出函数
    }

    ui->treeWidget->clear(); // 清空 QTreeWidget 中的先前结果
    itemRoot = new QTreeWidgetItem(ui->treeWidget, QStringList(strIP)); // 创建根节点,显示 IP 地址
    myThread = new MyThread(strIP, intPort); // 创建新的扫描线程,传入 IP 和端口

    // 连接 myThread 的信号 send_scan_signal(int, bool) 和槽函数 recv_result(int, bool)
    connect(myThread, SIGNAL(send_scan_signal(int, bool)), this, SLOT(recv_result(int, bool))); // 连接信号和槽
    connect(myThread, &MyThread::finished, this, &MainWindow::onScanFinished); // 连接线程结束信号
    myThread->start(); // 启动扫描线程
}

在主窗口设置扫描结束端口

为finished增加两个参数int port, bool isOpen

1.在 MyThread.h 中,修改 finished() 信号的定义,以便它可以接收端口号和状态:

signals:  
    void send_scan_signal(int, bool); // 发送线程扫描结果  
    void finished(int port, bool isOpen); // 扫描完成的信号,带有端口号和状态

2.在 MyThread.cpp 中,修改 run() 方法,以便在扫描结束时发出 finished(int port, bool isOpen) 信号:

void MyThread::run()  
{  
    QTcpSocket socket(0);  
    bool lastPortStatus = false; // 用于存储最后一个端口的状态  
    int lastPort = m_intPort; // 用于存储最后一个端口号  

    for (int i = m_intPort; i <= 1024; i++) // 从 m_intPort 开始扫描,最大扫描到 1024  
    {  
        if (isStop)  
            return;  

        socket.abort();  
        socket.connectToHost(m_strIP, i);  
        if (socket.waitForConnected(1000))  
        {  
            emit send_scan_signal(i, true); // 发送信号  
            qDebug("%d: %s", i, "opened"); // 调试信息  
            lastPortStatus = true; // 更新最后一个端口状态  
            lastPort = i; // 更新最后一个端口号  
        }  
        else  
        {  
            emit send_scan_signal(i, false);  
            qDebug("%d: %s", i, "closed");  
            lastPortStatus = false; // 更新最后一个端口状态  
            lastPort = i; // 更新最后一个端口号  
        }  
        msleep(100);  
    }  

    emit finished(lastPort, lastPortStatus); // 在扫描完成后发出信号,传递最后的端口号和状态  
}

3.更新 MainWindow.h

private slots:  
    void onScanFinished(int port, bool isOpen); // 声明 onScanFinished 槽函数

4.更新 MainWindow.cpp

// 连接信号和槽  
    connect(myThread, &MyThread::send_scan_signal, this, &MainWindow::recv_result); // 连接扫描结果信号  
    connect(myThread, &MyThread::finished, this, &MainWindow::onScanFinished); // 连接扫描结束信号  

5.更新槽函数的实现

在 MainWindow 中实现 onScanFinished(int port, bool isOpen) 槽函数,以更新 UI:

void MainWindow::onScanFinished(int port, bool isOpen) {  
    QString strPort = QString::number(port); // 将端口号转换为字符串  
    QString status = isOpen ? "opened" : "closed"; // 根据状态设置字符串  
    // 在 UI 中显示端口状态  
    QMessageBox::information(this, "Scan Finished", "Port " + strPort + " is " + status, QMessageBox::Ok);  
    
    ui->pushButton_Scan->setEnabled(true); // 重新启用扫描按钮  
}

添加一点中文播报

则实现了结束扫描的提示

补充概念和技巧

线程的概念

“线程”是计算机科学中的一个重要概念,通常指的是程序执行的最小单位。线程可以被视为轻量级的进程,它们共享同一进程的资源(如内存和文件句柄),但每个线程都有自己的执行栈和程序计数器。

槽函数

槽函数用于响应信号。

调大qt creater的字体:

ctrl+shift++

确保在 on_pushButton_Scan_clicked() 函数中连接信号之前创建 myThread 对象


http://www.kler.cn/a/471547.html

相关文章:

  • JuiceFS 详解:一款为云原生设计的高性能分布式文件系统
  • C++ 中如何优雅地返回一个递归闭包函数?
  • Hbuilder ios 离线打包sdk版本4.36,HbuilderX 4.36生成打包资源 问题记录
  • 深度学习中多个损失怎么平衡?
  • 【iOS Swift Moya 最新请求网络框架封装通用】
  • github开源链游详细搭建文档
  • 【JavaWeb】2. 通用基础代码
  • ubuntu为Docker配置代理
  • 永磁同步电机模型预测控制——模型预测研究现状
  • ChatGPT在数据分析与处理中的使用详解
  • 在 a-tree 中报错 parent 期望是对象,但获得是字符串
  • PyTorch 框架实现线性回归:从数据预处理到模型训练全流程
  • vue 导出excel接口请求和axios返回值blob类型处理
  • Go语言的数据库交互
  • 最新版Chrome浏览器加载ActiveX控件之SolidWorks 3D控件
  • EasyExcel.read读取 Excel 文件
  • 第 24 章 网络请求与远程资源
  • WELL健康建筑认证在2025年相关消息
  • 网络安全主动防御技术与应用
  • 第14章 MySQL事务日志
  • 【物联网原理与运用】知识点总结(下)
  • 5G学习笔记之PNI-NPN
  • FastGPT 介绍
  • Olib开放图书 (zlibrary电子书在线搜索下载工具) 同步zlibrary
  • javaBC库冲突问题
  • 算能AI计算服务器SE5设备树的二次修改实操