QT——TCP网络调试助手

目录

一.项目展示

​编辑

二.开发流程

三.QTcpServer、QTcpSocket、QUdpSocket类的学习

1.QTcpServer服务端

2.QTcpSocket客户端

3.Udp通信

四.网络调试助手

1.首先我们实现当用户选择不同协议类型时不同的UI组件如何切换

2.实现打开/关闭按键图片的切换

方式一:通过其父类所提供的void setIcon(const QIcon &icon)函数去实现

方式二:重写QPushButton的事件

3.定时发送

 4.实现代码如下


一.项目展示

二.开发流程

三.QTcpServer、QTcpSocket、QUdpSocket类的学习

Tcp面向连接的基于字节流的协议(点对点)

Udp面向无连接的基于报文的协议(一对一、一对多、多对多)

3.1QTcpServer类中包含以下函数

listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0);用于监听

QTcpSocket nextPendingConnection();                        

从监听套接字中获取一个已准备好的客户端进行连接
deleteLater();

释放资源

disconnectFromHost(); 

断开连接

 QTcpServer类包含以下信号

newConnection();监听到有新的连接时
acceptError(QAbstractSocket::SocketError socketError);尝试接受新的连接时发生错误

 3.2QTcpSocket类包含以下函数

connectToHost(const QHostAddress &address = QHostAddress::Any, quint16 port = 0);连接服务端

QHostAddress peerAddress();

获取ip地址
quint16 clientPort();获取端口号
readall();读取消息
write(QByteArray data);发送数据
state();判断当前状态,如:if(socket->state== QAbstractSocket::ConnectedState)
deleteLater();释放资源

 QTcpSocket类包含以下信号

connected();成功连接
readyRead();有新的数据发来时
disconnected();断开连接时

3.3QUdpSocket类包含以下函数

bind(const QHostAddress &address = QHostAddress::Any, quint16 port = 0);绑定ip地址和端口,接受数据时使用
bool hasPendingDatagrams() const;用于检查是否有待处理的数据报。返回 true: 表示有一个或多个待处理的数据报
pendingDatagramSize();通常用于设置接受缓冲区的大小时使用,返回下一个待处理的数据报的大小
readDatagram(buffer.data(), buffer.size(), &sender, &senderPort); 将接收到的数据存储到提供的缓冲区中
writeDatagram(<要发送的数据>, <ip地址>, <端口号>); ip为QHostAddress::Broadcast代表广播发送
close();关闭udp套接字
deleteLater();释放资源

 QUdpSocket类包含以下信号

readyRead();有新的数据发来时

3.4以下程序可获取本地ip地址

// 获取本地主机名称
QString localHostName = QHostInfo::localHostName(); 

// 根据本地主机名称获取本地主机信息
QHostInfo info = QHostInfo::fromName(localHostName); 

// 将本地主机的所有 IP 地址赋值给容器中
QList<QHostAddress> addresses = info.addresses(); 

// 清空 comboBox_2 并添加 IPv4 地址
ui->comboBox_2->clear(); 
for (const auto& address : addresses) {
    if (address.protocol() == QAbstractSocket::IPv4Protocol) {
        qDebug() << "IPv4 Address:" << address.toString();
        ui->comboBox_2->addItem(address.toString());
    }
}

1.QTcpServer服务端

QTcpServer* server = new QTcpServer(this);               //创建一个监听套接字
server->listen();                                        //开始监听指定ip与端口

connect(server, &QTcpServer::newConnection, this, [=](){ //信号与槽连接:当监听到有新的连接时
    QTcpSocket* clientSocket;                            //创建一个网络套接字
    clientSocket = server->nextPendingConnection();      //从监听套接字中获取一个已准备好的客户端进行连接
                                                         //获取客户端的ip与端口号
    QHostAddress clientAddress = clientSocket->peerAddress();
    quint16 clientPort = clientSocket->peerPort();
                                                         //信号与槽连接:当有新的消息时
    connect(clientSocket, &QTcpSocket::readyRead, this, [=](){
        QByteArray data = clientSocket->readAll();       //读取消息
    );
                                                         //信号与槽连接:当客户端断开连接时
    connect(clientSocket, &QTcpSocket::disconnected, this, [=](){
        clientPort->deleteLater();                       //释放资源
    );
    if(clientSocket->state() == QAbstractSocket::ConnectedState){
        cnt = clientSocket->write(arrayData);
    }
);

server->close();                                         //关闭服务端

2.QTcpSocket客户端

QTcpSocket* socket = new QTcpSocket(this);
connect(socket, &QTcpSocket::connected, this, [=]() {       //客户端连接到服务端槽函数
    //...
});

connect(socket, &QTcpSocket::readyRead, this, [=]() {       //客户端接收到数据槽函数
    int sum = 0;
    QByteArray data = socket->readAll();                    //读取接收到的数据
    sum = data.size();
}

connect(socket, &QTcpSocket::disconnected, this, [=]() {    //客户端与服务端断开连接槽函数
    qDebug() << "Disconnected from server.";
    socket->deleteLater();                                  //断开连接时删除 socket
});

if(socket->state() == QAbstractSocket::ConnectedState){     //客户端发送数据
    socket->write(arrayData);
}

socket->connectToHost(remIpaddress, remPort);               //尝试连接服务端

socket->disconnectFromHost();                               //主动断开与客户端的连接
socket->deleteLater();                                      //释放资源

3.Udp通信

QUdpSocke* udp = new QUdpSocket(this);                   
udp->bind(ipaddress, honts.toInt();             //接受数据时使用,绑定ip地址和端口
                                                //绑定信号与槽,当接收到数据时 
connect(udp, &QUdpSocket::readyRead, this, [=](){                                               
    while (udp->hasPendingDatagrams()) {        //检查是否有待处理的数据报,循环读取
        QByteArray buffer;
        buffer.resize(int(udp->pendingDatagramSize()));   //确保接收缓冲区大小合适
        QHostAddress sender;                    //发送者的地址
        quint16 senderPort;                     //发送者的端口
                                                //读取数据并存入buffer中
        udp->readDatagram(buffer.data(), buffer.size(), &sender, &senderPort); 
    }
);

udp->writeDatagram(<要发送的数据>, <ip地址>, <端口号>); 

QHostAddress broadcastAddress = QHostAddress::Broadcast;                     // 设置广播地址
qint64 bytesWritten = con->writeDatagram(arrayData, broadcastAddress, port); // 发送广播

udp->close();                                      
udp->deleteLater();                                

四.网络调试助手

1.首先我们实现当用户选择不同协议类型时不同的UI组件如何切换

绑定信号&QComboBox::currentIndexChangedQComBobox组件,当QComboBox组件的索引变化时,执行下面槽函数,实现不同的UI组件之间的切换

void Widget::on_CurrentIndexChanged() {
    // 获取用户当前选择的索引
    currentIndex = ui->comboBox_1->currentIndex();

    // 删除垂直布局中的第六个组件(如果存在)
    if (ui->verticalLayout->count() >= 6) {
        QWidget* widget = ui->verticalLayout->itemAt(5)->widget(); // 获取第六个控件
        if (widget) {
            ui->verticalLayout->removeWidget(widget);   // 从布局中移除
            widget->deleteLater();                       // 删除组件
        }
    }

    // 根据选择的索引更新界面
    if (currentIndex == 1) {  // 用户选择了客户端
        ui->label_2->setText("(2)本地主机地址");
        ui->label_3->setText("(3)远程主机地址");

        box = new QComboBox(this);                    // 创建新的 QComboBox
        box->addItem("192.168.56.1 :8080");           // 向组件中添加内容
        box->setEditable(true);                        // 设置组件中的内容可修改

        ui->verticalLayout->addWidget(box);            // 将组件添加到垂直布局中
        box->show();                                   // 显示新创建的 QComboBox

    } else if (currentIndex == 0) {  // 用户选择了UDP
        ui->lineEdit_rev->setText("192.168.238.1");
        ui->lineEdit_revHonts->setText("8080");
        ui->label_2->setText("(2)本地主机地址");
        ui->label_3->setText("(3)本地主机端口");

        edt = new QLineEdit(this);                     // 创建新的 QLineEdit
        edt->setText("8080");                          // 设置新的 QLineEdit 显示的内容

        ui->verticalLayout->addWidget(edt);            // 将组件添加到垂直布局中
        edt->show();                                   // 显示新创建的 QLineEdit

    } else {  // 用户选择了服务端
        ui->label_2->setText("(2)本地主机地址");
        ui->label_3->setText("(3)本地主机端口");

        edt = new QLineEdit(this);                     // 创建新的 QLineEdit
        edt->setText("8080");                          // 设置新的 QLineEdit 显示的内容

        ui->verticalLayout->addWidget(edt);            // 将组件添加到垂直布局中
        edt->show();                                   // 显示新创建的 QLineEdit
    }
}

2.实现打开/关闭按键图片的切换

方式一:通过其父类所提供的void setIcon(const QIcon &icon)函数去实现

const QString ICON_PATH_MM = ":/pictures/mm.png"; // 图标路径常量
const QString ICON_PATH_PP = ":/pictures/pp.png"; // 图标路径常量

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    ui->pushButton->setCheckable(true);

    // 设置按钮透明背景
    ui->pushButton->setStyleSheet("QPushButton {"
                                  "background-color: transparent;"
                                  "border: none;" // 可选:去掉按钮边框
                                  "}");

    // 设置图标和大小
    setButtonIconAndSize(ICON_PATH_MM);
}

void Widget::on_pushButton_clicked(bool checked)
{
    // 根据按钮的 checked 状态切换图标
    setButtonIconAndSize(checked ? ICON_PATH_PP : ICON_PATH_MM);
}

// 封装设置图标和大小的逻辑
void Widget::setButtonIconAndSize(const QString &iconPath)
{
    QSize buttonSize = ui->pushButton->size(); // 获取按钮的当前大小
    ui->pushButton->setIconSize(buttonSize);   // 设置图标大小
    ui->pushButton->setIcon(QIcon(iconPath));  // 设置图标
}

方式二:重写QPushButton的事件

bool t = true;

myBtnOpen::myBtnOpen(QWidget *parent):QPushButton(parent)
{
    // 加载初始图片
    pic.load(":/pictures/mm.png");
    // 设置按钮的固定大小为图片的大小
    //setFixedSize(pic.size());
    //setFixedSize(100,100);
    // 刷新界面,触发 paintEvent 进行绘制
    update();
}

void myBtnOpen::paintEvent(QPaintEvent *e)
{
    // 创建一个 QPainter 对象,负责绘制图片
    QPainter painter(this);
    // 使用 QPainter 在按钮区域内绘制当前加载的图片
    painter.drawPixmap(rect(), pic);
}

void myBtnOpen::mousePressEvent(QMouseEvent *e)
{
    if(e->button() == Qt::LeftButton){
        //打开
        if(t == true){
            pic.load(":/pictures/mm.png");
            update();
            t = false;
        }else{
            pic.load(":/pictures/pp.png");
            update();
            t = true;
        }
    }
    QPushButton::mousePressEvent(e);
}

3.定时发送

通过定时器实现,当用户勾选定时发送组件时

1.初始化定时器,设置定时器定时时间,启动定时器。

2.在构造函数中绑定定时器的超时信号与槽函数,通过Lambda表达式调用发送函数既可以

//定时发送槽函数
void Widget::on_checkBox_10_clicked(bool checked)
{
    if(checked){
        //设置定时器定时时间
        timer->setInterval(ui->lineEdit_msData->text().toInt());
        //启动定时器
        timer->start();
    }else{
        //停止定时器
        timer->stop();
    }
}
//发送按键槽函数
void Widget::on_pushButton_send_clicked()
{
    //(1)先获取用户输入的要发送的内容
    QString data = ui->textEdit_sendData->toPlainText();
    QByteArray arrayData = data.toLocal8Bit();
    int cnt = 0;
    //(2)判断Hex发送是否勾选,此处为勾选
    if (ui->checkBox_9->isChecked()) {
        // a.检查字节数是否是偶数
        if (0 != arrayData.size() % 2) {
            ui->label_state->setText("input error!");
            return;
        }
        // b.检查是否符合Hex表达
        for (char c : arrayData) {
            if (!isxdigit(c)) {
                ui->label_state->setText("input error!");
                return;
            }
        }
        // c.转化为16进制
        arrayData = QByteArray::fromHex(arrayData);
    }
    //(3).开始发送
    if (t == 1) {        // 客户端发送数据
        cnt = socket->write(arrayData);
    } else if (t == 2) { // 服务端发送数据
        cnt = clientSocket->write(arrayData);
    }

    //(4)判断是否发送成功
    if (cnt == -1) {
        ui->label_state->setText("send error!");
    } else {
        if (ui->checkBox_8->isChecked()) {
            ui->textEdit_sendData->clear();
        }
        sendCnt += cnt;
        ui->label_state->setText("send OK!");
        ui->label_sendCnt->setText("发送: " + QString::number(sendCnt));
    }
}

 4.实现代码如下

tunnek/mi (github.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/907901.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

eclipse下载与安装(汉化教程)超详细

目录 一、下载eclipse安装包 三、配置eclipse 代码自动补全功能 安装汉化包 中英文切换 四、用eclipse写hello world 一、下载eclipse安装包 1、首先进入 eclipse官网 如下&#xff1a; 2、这里面有很多版本&#xff1b;我们小白一般选择第二个&#xff0c;向下滑动&…

[FE] React 初窥门径(四):React 组件的加载过程(render 阶段)

1. 回顾 前几篇文章中&#xff0c;我们采用了 VSCode 插件 CodeTour 来记录代码的执行过程&#xff0c; 并把相关的数据 .tour/ 放到了 github: thzt/react-tour 中。 截止到本文为之&#xff0c;我们总共记录了这些 code-tour&#xff0c; .tour/ ├── 2. 构建过程.tour ├─…

什么是数字签名技术?

信息安全五要素 名称说明机密性机密性是指网络信息不泄露给非授权的用户、实体或程序&#xff0c;能够防止非授权者获取信息完整性完整性是指网络信息或系统未经授权不能进行更改的特性可用性可用性是指合法许可的用户能够及时获取网络信息或服务的特性可控性可控性是指可以控…

人工智能原理实验一:知识的表示与推理实验

一、实验目的 本实验课程是计算机、智能、物联网等专业学生的一门专业课程&#xff0c;通过实验&#xff0c;帮助学生更好地掌握人工智能相关概念、技术、原理、应用等&#xff1b;通过实验提高学生编写实验报告、总结实验结果的能力&#xff1b;使学生对智能程序、智能算法等有…

虚拟机 Email 恢复专用工具:Virtual Machine Email Recovery

天津鸿萌科贸发展有限公司从事数据安全服务二十余年&#xff0c;致力于为各领域客户提供专业的数据恢复、数据备份解决方案与服务&#xff0c;并针对企业面临的数据安全风险&#xff0c;提供专业的相关数据安全培训。 天津鸿萌科贸发展有限公司是 SysTools 系列数据恢复、取证及…

HTML 基础标签——表单标签<form>

文章目录 1. `<form>` 标签:定义表单容器2. `<input>` 标签:多用途输入控件3. `<textarea>` 标签:多行文本输入框4. `<select>` 标签:下拉选择框5. `<option>` 标签:下拉菜单选项6. `<button>` 标签:按钮元素7. `<label>` 标签…

使用Jupyter Notebook进行数据科学项目

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 使用Jupyter Notebook进行数据科学项目 Jupyter Notebook 简介 安装 Jupyter Notebook 创建和管理 Notebook 编写和运行代码 示例…

详解RabbitMQ三种队列类型

RabbitMQ 是一个强大的消息队列系统&#xff0c;它提供了多种队列类型以满足不同的使用需求。本文将探讨三种主要队列类型&#xff1a;经典队列、仲裁队列和流式队列&#xff0c;并讨论它们的区别和选型建议。 经典队列&#xff08;Classic Queues&#xff09; 简介&#xff…

【AD】2-1 元件符号的绘制创建实例-电阻容/CHIP类器件

1.新建工程后&#xff0c;双击原理图库&#xff0c;点击Panels后&#xff0c;选择SCH Library&#xff0c;双击元器件可在右侧进行更改名称 2.点击视图&#xff0c;栅格&#xff0c;设置捕捉栅格为100mil 3.点击放置管脚&#xff0c;可按空格键进行旋转&#xff0c;按TAB键可以…

JDBC2(防止sql注入,数据库连接池)

防止SQL注入 sql注入&#xff1a;利用sql语句的语法特点&#xff0c;应用层输入特殊格式&#xff0c;让原有的sql语句失效 创建表结构 并加入数据 create table login(lid int primary key auto_increment,lname varchar(20),lpwd varchar(20),lsex varchar(2),laddr varcha…

基于SSM+微信小程序的订餐管理系统(点餐2)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 基于SSM微信小程序的订餐管理系统实现了管理员和用户。管理端实现了 首页、个人中心、用户管理、菜品分类管理、菜品信息管理、订单信息管理、配送信息管理、菜品评价管理、订单投诉管理、…

MRCTF2020:你传你ma呢

文件上传题先判断黑白名单过滤&#xff0c;先传个最简单的木马 这里上传不了php文件&#xff0c;猜测可能是对php文件进行了过滤&#xff0c;将文件改为任意后缀这里改为.abc 还是上传不成功&#xff0c;猜测可能对MIME也做了过滤&#xff0c;将Content-Type更改为image/jpeg再…

设计模式09-行为型模式2(状态模式/策略模式/Java)

5.4 状态模式 5.4.1 状态模式的定义 1.模式动机&#xff1a;有些对象具有多种状态&#xff0c;这些状态在某些情况下能够相互转换&#xff0c;对象在不同的状态下将具有不同的行为&#xff0c;将拥有状态的对象中和状态的行为分离。 2.模式定义&#xff1a;允许一个对象在其…

tauri中shell的特殊字符

tauri中shell的特殊字符 官网例子&#xff1a;https://tauri.app/plugin/shell/ 中的入参是 \S 入参&#xff0c;但如果入参存在空格等特殊字符串&#xff0c;将无法传入 "permissions": [{"identifier": "shell:allow-execute","allow&qu…

电子电气架构 --- Trace 32(劳特巴赫)多核系统的调试

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 所有人的看法和评价都是暂时的,只有自己的经历是伴随一生的,几乎所有的担忧和畏惧,都是来源于自己的想象,只有你真的去做了,才会发现有多快乐。…

使用Web Workers实现JavaScript的多线程编程

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 使用Web Workers实现JavaScript的多线程编程 引言 Web Workers 简介 创建和使用 Worker 通信机制 主线程向 Worker 发送消息 Wor…

【uni-app】创建自定义模板

1. 步骤 打开自定义模板文件夹 在此文件夹下创建模板文件&#xff08;注意后缀名&#xff09; 重新点击“新建页面” 即可看到新建的模板 2. 注意事项 创建的模板必须文件类型对应&#xff08;vue模板就创建*.vue文件, uvue模板就创建*.uvue文件&#xff09;

【云原生】Docker搭建开源翻译组件Deepl使用详解

目录 一、前言 二、微服务项目使用翻译组件的场景 2.1 多语言用户界面 2.2 业务逻辑中的翻译需求 2.3 满足实时通信的要求 2.4 内容管理系统 2.5 个性化推荐系统 2.6 日志和监控 三、开源类翻译组件解决方案 3.1 国内翻译组件方案汇总 3.1.1 百度翻译 3.1.2 腾讯翻…

Hms?: 1渗透测试

靶机&#xff1a;Hms?: 1 Hms?: 1 ~ VulnHub 攻击机&#xff1a;kail linux 2024 主机扫描阶段发现不了靶机&#xff0c;所以需要按DriftingBlues2一样手动配置网卡 1,将两台虚拟机网络连接都改为NAT模式&#xff0c;并查看靶机的MAC地址 2&#xff0c;攻击机上做主机扫描发现…

linux-环境变量

环境变量是系统提供的一组 name value 的变量&#xff0c;不同的变量有不同的用途&#xff0c;通常都具有全局属性 env 查看环境变量 PATH PATH是一个保存着系统指令路径的一个环境变量&#xff0c;系统提供的指令不需要路径&#xff0c;直接就可以使用就是因为指令的路径…