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

QT——TCP网络调试助手

一.项目概述

  • 学习QTcpServer
  • 学习QTcpClicent
  • 学习TextEdit特定位置输入的文字颜色
  • 学习网络通信相关知识点

二.开发流程

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

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

// 当 QComboBox 组件索引发生改变时的槽函数
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)远程主机地址");// 获取本地主机地址QString localHostName = QHostInfo::localHostName();QHostInfo info = QHostInfo::fromName(localHostName);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());}}// 创建并设置新的 QComboBoxQComboBox* box = new QComboBox(this);box->addItem("192.168.56.1 :8080");box->setEditable(true);  // 设置为组件中的内容可修改// 将组件添加到垂直布局中ui->verticalLayout->addWidget(box);box->show();  // 显示新创建的 QComboBox} else if (currentIndex == 2) { // 用户选择了服务端=================================ui->label_2->setText("(2)本地主机地址");ui->label_3->setText("(3)本地主机端口");// 创建并设置新的 QLineEditQLineEdit* edt = new QLineEdit(this);edt->setText("8080");// 将组件添加到垂直布局中ui->verticalLayout->addWidget(edt);edt->show();  // 显示新创建的 QLineEdit}else{ui->label_2->setText("(2)本地主机地址");ui->label_3->setText("(3)本地主机端口");// 创建并设置新的 QLineEditQLineEdit* edt = new QLineEdit(this);edt->setText("8080");// 将组件添加到垂直布局中ui->verticalLayout->addWidget(edt);edt->show();  // 显示新创建的 QLineEdit}
}

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

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

#include "widget.h"
#include "ui_widget.h"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);
}Widget::~Widget()
{delete ui;
}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的事件

#include "mybtnopen.h"
#include <QPainter>
#include <QMouseEvent>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.打开/关闭按键具体实现逻辑

  • 首先在构造函数中执行以下代码获取本地主机ip地址并显示到QComboBox组件中
void Widget::getLocalhostIpAddress()
{QString localHostName = QHostInfo::localHostName();          //获取本地主机名称QHostInfo info = QHostInfo::fromName(localHostName);         //根据本地主机名称获取本地主机信息QList<QHostAddress> addresses = info.addresses();            //将本地主机的所有ip地址赋值给容器中// 清空 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.服务端有新的连接
  • 2.客户端连接成功
  • 3.客户端接收到新数据
  • 4.客户端与服务端断开连接
server = new QTcpServer(this);
connect(server, &QTcpServer::newConnection, this, &Widget::on_newConnection); // 服务端有新连接时槽函数connect(socket, &QTcpSocket::connected, this, [=]() { // 客户端连接到服务端槽函数ui->btn_open->setIcon(QIcon(":/pictures/pp.png"));ui->comboBox_1->setEnabled(false);
});connect(socket, &QTcpSocket::readyRead, this, [=]() { // 客户端接收到数据槽函数int sum = 0;QByteArray data = socket->readAll(); // 读取接收到的数据sum = data.size();revCnt += sum;qDebug() << "Received from server:" << data;if (ui->checkBox_2->isChecked()) {data.append("\r\n");}if (ui->checkBox_4->isChecked()) { // Hex显示是否勾选,此处为勾选QByteArray tmpHexString = data.toHex().toUpper();ui->textEdit->insertPlainText(tmpHexString);} else {ui->textEdit->insertPlainText(data);}ui->label_revCnt->setText("接受: " + QString::number(revCnt));
});connect(socket, &QTcpSocket::disconnected, this, [=]() { // 客户端与服务端断开连接槽函数qDebug() << "Disconnected from server.";socket->deleteLater(); // 断开连接时删除 socket
});

打开/关闭按键槽函数分为三大部分(服务端、客户端、UDP)

//打开按键槽函数
void Widget::on_btn_open_clicked(bool checked)
{if(checked){//获取本地主机ip地址QString localIpAddress = ui->comboBox_2->currentText();QHostAddress ipaddress = (QHostAddress)localIpAddress;if(ui->comboBox_1->currentIndex() == 2){      //当前所选的是服务端===================================================QString honts = edt->text();qDebug() << "localIpAddress: " << localIpAddress;qDebug() << "honts: " << honts;quint16 port = honts.toUInt();server->listen(ipaddress, port); //开始监听ui->btn_open->setIcon(QIcon(":/pictures/pp.png"));ui->comboBox_1->setEnabled(false);t = 2;}else if(ui->comboBox_1->currentIndex() == 1){ //当前所选的是客户端===================================================QHostAddress remIpaddress;                 //远程主机ip地址quint16 remPort;                           //远程主机端口号//获取用户输入的远程主机ip地址与端口号QString data = box->currentText();// 使用 ':' 字符分割字符串QStringList parts = data.split(":");if (parts.size() == 2) {QString ipAddress = parts[0];QString port = parts[1];remIpaddress = (QHostAddress)ipAddress;   //远程主机ip地址remPort = port.toUInt();                  //远程主机端口号} else {qDebug() << "Invalid address format!";}socket = new QTcpSocket;socket->connectToHost(remIpaddress, remPort); //尝试连接服务端t = 1;}else{                                         //当前所选的是UDP===================================================QString honts = edt->text();qDebug() << "localIpAddress: " << localIpAddress;qDebug() << "honts: " << honts;t = 0;}}else{ui->btn_open->setIcon(QIcon(":/pictures/mm.png")); // 设置按钮图标ui->comboBox_1->setEnabled(true); // 启用下拉框if (t == 1) { // 断开客户端连接if (socket) {socket->disconnectFromHost(); // 断开与服务器的连接socket->waitForDisconnected(); // 等待断开完成socket->deleteLater(); // 删除 socketqDebug() << "Client socket disconnected.";}} else if (t == 2) { // 断开服务端连接if (server->isListening()) {server->close(); // 关闭服务器qDebug() << "Server stopped listening.";}}}}

服务端有新的连接的槽函数如下

void Widget::on_newConnection()
{// 获取新连接clientSocket = server->nextPendingConnection();if (clientSocket) {qDebug() << "New connection from:" << clientSocket->peerAddress().toString() << ":" << clientSocket->peerPort();// 连接信号和槽connect(clientSocket, &QTcpSocket::readyRead, this, [=]() {int sum = 0;QByteArray data = clientSocket->readAll();qDebug() << "Received data:" << data;sum = data.size();revCnt += sum;ui->label_revCnt->setText("接受: "+ QString::number(revCnt));if(ui->checkBox_4->isChecked()){             //Hex显示是否勾选,此处为勾选QByteArray tmpHexString = data.toHex().toUpper();if(ui->checkBox_2->isChecked()) tmpHexString.append("\r\n");ui->textEdit->append(tmpHexString);}else{if(ui->checkBox_2->isChecked()) data.append("\r\n");ui->textEdit->append(data);}});connect(clientSocket, &QTcpSocket::disconnected, this, [=]() {qDebug() << "Client disconnected:" << clientSocket->peerAddress().toString() << ":" << clientSocket->peerPort();clientSocket->deleteLater(); // 断开连接时删除 socket});} else {qDebug() << "Failed to accept new connection.";}
}

4.发送数据具体实现逻辑

4.1首先实现正常发送

//发送按键槽函数
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.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));}
}


http://www.mrgr.cn/news/62016.html

相关文章:

  • C语言:函数指针(详细版)
  • 基于阿里云服务的移动应用日志管理方案—日志的上传、下载、存档等
  • C语言教程——数组(2)
  • 活着就好20241030
  • QT实时显示日志内容
  • 【ACM出版,EI稳定检索,九大高校联合举办, IEEE Fellow支持】2024年计算机视觉与艺术研讨会(CVA 2024)
  • 创建ODBC数据源SQLConfigDataSource函数的用法
  • gpio子系统-通过io来控制gpio
  • 刚刚买的域名被DNS劫持了怎么处理
  • Spring 设计模式之装饰器模式
  • Unreal5从入门到精通之如何解决在VR项目在头显中卡顿的问题
  • 基于vue框架的的家政预定服务系统4k26i(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。
  • 万圣节活动如何实现在线预约报名?
  • uniapp iOS打包证书过期——重新下载证书及更新文件
  • 设计模式 - 工厂方法模式
  • Shell变量与子串
  • Mac程序坞窗口预览的方法来了
  • Rust 力扣 - 59. 螺旋矩阵 II
  • 为什么美业必须要有一套专业的美业门店管理系统?美业SaaS系统收银系统拓客系统Java源码
  • Django框架实现用户认证
  • 【力扣专题栏】两两交换链表中的节点,如何实现链表中两两相邻节点的交换?
  • 在JavaScript中怎样实现闭包?
  • 5、片元着色器之基础光照模型:Phong模型和Blinn-Phong模型
  • 【Linux】进程间通信
  • iOS18 取消/适配TabbarController缩放动画
  • Vue CLI: 安装、项目创建及基本概念指南,vue生命周期