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

基于TCP/QT/C++的网络调试助手测试报告

目录

一、项目介绍

1、项目背景

2、项目功能

3、开发流程

二、UI设计

1、服务端

2、客户端

三、代码实现

1、QT中使用TCP的关键流程

2、服务器开发

3、客户端开发

四、结果测试

五、项目总结


一、项目介绍

1、项目背景

在网络应用开发和嵌入式系统调试过程中,开发者经常需要模拟 客户端-服务器 交互,以验证数据传输的正确性和稳定性。然而,传统的网络调试工具通常难以同时支持多个客户端的管理,或者缺乏灵活的调试能力。因此,本项目基于 Qt 框架实现了一款 功能强大、易于使用 的网络调试助手,旨在提高开发和测试效率。

2、项目功能

  •  支持TCP连接:使用Socket网络编程,客户端和服务器的连接,是通过网络连接起来的,数据传输方式TCP,比较可靠。

  • 监听指定 IP 和端口,支持多个客户端连接

  • 管理多个客户端,支持客户端连接、断开及状态监控

  • 收发消息,支持单个或所有客户端的消息广播

  • 断开连接及停止监听,确保资源释放

本项目为网络开发人员提供了一个高效、直观的调试工具,能够大幅提升 网络通信协议的调试效率,同时也可用于 嵌入式设备调试、物联网设备通信测试 等场景。

3、开发流程

二、UI设计

1、服务端

使用的控件命名如下图所示:

2、客户端

使用的控件命名如下图所示:

在设计布局的时候,可以大体上从上到下分几块,每个小块里面先水平布局,每个小块布局好之后,在所有的小块进行垂直布局,在调整每个小块的占比,可以一点一点调整,上下左右再留一点空白,这样布局会看着比较舒服。

这样设计出来的窗口是固定大小,那么怎样可以在用户使用的时候,鼠标拉动窗口,控件可以跟着改变呢?

其实很简单,只需要一行代码就可以搞定,在Widget的构造方法中添加:

this->setLayout(ui->verticalLayout);

三、代码实现

QTcpServer 是 Qt 框架中用于实现 TCP 服务器功能的类,属于 QtNetwork 模块。它是 Qt 网络编程的重要组成部分,主要用于创建和管理 TCP 服务器端。

主要功能

  • TCP 服务端监听:使用 QTcpServer 可以轻松地创建一个 TCP 服务器,监听特定的 IP 地址和端口。

  • 连接管理:当客户端尝试连接到服务器时,QTcpServer 提供信号(如 newConnection())通知应用程序有新的连接请求。你可以通过重写虚函数或使用信号与槽来处理这些事件。

  • 数据通信:连接建立后,可以通过派生自 QTcpSocket 的套接字类来进行客户端和服务器之间的数据收发。

  • 多线程支持:Qt 的网络模块默认是异步的,这意味着服务器可以在不阻塞主线程的情况下处理多个连接。

1、QT中使用TCP的关键流程

首先需要在创建项目后,在.pro文件中加入network网络权限

2、服务器开发

#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
#include <QNetworkInterface>
#include<QTcpSocket>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    //初始化 UI 界面的代码,它的作用是加载 Qt Designer 设计的 UI 布局,并将界面元素绑定到 ui 指针中
    ui->setupUi(this);
    //布局自适应窗口
    this->setLayout(ui->verticalLayout);
    
    //实例化一个QTcpServer对象
    server = new QTcpServer(this);

    //绑定信号与槽
    connect(ui->comboBoxChildren,&MyComboBox::on_comboBox_clicked,this,&Widget::mComboBox_refresh);
    connect(server,SIGNAL(newConnection()),this,SLOT(on_newClient_connect()));
    //一些细节的修改,在没有客户端连接的时候,停止监听、断开连接以及发送按钮不可用
    ui->btnLineout->setEnabled(false);
    ui->btnStopListen->setEnabled(false);
    ui->btnSend->setEnabled(false);

    //获取本机的所有网络接口地址,并将 IPv4 地址添加到 comboBoxAddr 下拉框中,供用户选择服务器监听的地址。
    QList<QHostAddress> addresss = QNetworkInterface::allAddresses();
    for(QHostAddress tmp :addresss){
        if(tmp.protocol()==QAbstractSocket::IPv4Protocol){
            ui->comboBoxAddr->addItem(tmp.toString());
        }
    }

}

Widget::~Widget()
{
    //析构
    delete ui;
}

void Widget::on_newClient_connect()
{
    qDebug()<<"newClient In!";
    //如果有新连接进来
    if(server->hasPendingConnections()){
        //获得客户端连接信息
        QTcpSocket *connection = server->nextPendingConnection();
        qDebug()<<"client Addr:"<< connection->peerAddress().toString() <<"port:"<<connection->peerPort();
        ui->textEditRev->insertPlainText("客户端地址:"+connection->peerAddress().toString()+
                                         "\n客户端端口号:"+QString::number(connection->peerPort())+"\n");
        connect(connection,SIGNAL(readyRead()),this,SLOT(on_readyRead_handler()));
        connect(connection,SIGNAL(disconnected()),this,SLOT(mdisconnected()));
        connect(connection,SIGNAL(stateChange(QAbstractSocket::SocketState)),
                this,SLOT(mststeChanged(QAbstractSocket::SocketState socketState)));
        //有新连接进来的时候在comboBoxChildren添加新条目,并显示当前最新进入的连接
        ui->comboBoxChildren->addItem(QString::number(connection->peerPort()));
        ui->comboBoxChildren->setCurrentText(QString::number(connection->peerPort()));
        
        if(!ui->btnSend->isEnabled()){
            ui->btnSend->setEnabled(true);
        }
    }
}

void Widget::on_readyRead_handler()
{
    //获取当前连接对象,sender() 返回当前触发信号的对象,但它是一个 QObject* 类型。转换为 QTcpSocket*。
    QTcpSocket *tmpSock = qobject_cast<QTcpSocket*>(sender());
    QByteArray revData = tmpSock->readAll();
    //光标移到末尾
    ui->textEditRev->moveCursor(QTextCursor::End);
    ui->textEditRev->ensureCursorVisible();
    //将数据显示到textEditRev
    ui->textEditRev->insertPlainText("客户端:"+revData);
}

void Widget::mdisconnected()
{
    //当客户端断开连接时,删除相应的 QTcpSocket 对象,并在 UI 上显示 客户端断开!
    QTcpSocket *tmpSock = qobject_cast<QTcpSocket *>(sender());
    ui->textEditRev->insertPlainText("客户端断开!");
    tmpSock->deleteLater();
}

void Widget::mststeChanged(QAbstractSocket::SocketState socketState)
{
    int tmpIndex;
    QTcpSocket *tmpSock = qobject_cast<QTcpSocket *>(sender());
    switch (socketState) {
    //如果客户端断开,通过端口号查找comboBoxChildren的索引,并删除条目,最后删除相应的 QTcpSocket 对象
    case QAbstractSocket::UnconnectedState:
        //    case QAbstractSocket::ClosingState:
        ui->textEditRev->insertPlainText("客户端断开!");
        tmpIndex = ui->comboBoxChildren->findText(QString::number(tmpSock->peerPort()));
        ui->comboBoxChildren->removeItem(tmpIndex);
        tmpSock->deleteLater();
        //如果所有客户端都断开连接后,发送键不可用
        if(ui->comboBoxChildren->count()==0)
            ui->btnSend->setEnabled(false);
        break;
    case QAbstractSocket::ConnectedState:
    case QAbstractSocket::ConnectingState:
        ui->textEditRev->insertPlainText("客户端接入!");
        break;

    }
}

void Widget::mComboBox_refresh()
{
    ui->comboBoxChildren->clear();
    //接收所有连接的客户端,将端口号添加下拉框条目中
    QList<QTcpSocket*> tcpSocketClients = server->findChildren<QTcpSocket*>();
    for(QTcpSocket* tmp:tcpSocketClients){
        if(tmp!=nullptr)
            if(tmp->peerPort()!=0)
                ui->comboBoxChildren->addItem(QString::number(tmp->peerPort()));
    }
    //all支持向所有客户端通信
    ui->comboBoxChildren->addItem("all");
}


void Widget::on_btnListen_clicked()
{
    //服务器开始监听指定的 IP 地址和端口,等待客户端连接
    //    QHostAddress addr("");
    int port = ui->lineEditPort->text().toInt();
    //    if(!server->listen(QHostAddress::Any,port))
    if(!server->listen(QHostAddress(ui->comboBoxAddr->currentText()),port)){
        qDebug()<< "listenError";
        QMessageBox msgBox;
        msgBox.setWindowTitle("监听失败");
        msgBox.setText("端口号被占用");
        msgBox.exec();
        return;
    }
    
    //监听键不可用,停止监听、断开连接可用
    ui->btnListen->setEnabled(false);
    ui->btnLineout->setEnabled(true);
    ui->btnStopListen->setEnabled(true);

}

void Widget::on_btnSend_clicked()
{
    QList<QTcpSocket*> tcpSocketClients = server->findChildren<QTcpSocket*>();
    //如果当前无连接,提示发送失败
    if(tcpSocketClients.isEmpty()){
        QMessageBox msgBox;
        msgBox.setWindowTitle("发送错误!");
        msgBox.setText("当前无连接!");
        msgBox.exec();
        ui->btnSend->setEnabled(false);
        return;
    }
    if(ui->comboBoxChildren->currentText() != "all"){
        //根据用户选择,找到指定客户端进行通信,通过childIndex来查找,该变量在用户选择条目后触发的on_comboBoxChildren_activated去赋值
        QString currentName = ui->comboBoxChildren->currentText();
        for(QTcpSocket* tmp:tcpSocketClients){
            if(QString::number(tmp->peerPort())==currentName){
                tmp->write(ui->textEditSend->toPlainText().toStdString().c_str());
            }
        }
//        tcpSocketClients[childIndex]->write(ui->textEditSend->toPlainText().toStdString().c_str());
    }else{
        //遍历所有子客户端,并一一调用write函数,向所有客户端发送消息
        for(QTcpSocket* tmp:tcpSocketClients){
            tmp->write(ui->textEditSend->toPlainText().toStdString().c_str());
        }
    }

}


void Widget::on_btnStopListen_clicked()
{
    //关闭所有的客户端连接
    QList<QTcpSocket*> tcpSocketClients = server->findChildren<QTcpSocket *>();
    for(QTcpSocket* tmp:tcpSocketClients){
        tmp->close();
    }
    //关闭服务器,停止监听
    server->close();
    ui->btnListen->setEnabled(true);
    ui->btnLineout->setEnabled(false);
    ui->btnStopListen->setEnabled(false);
}

void Widget::on_btnLineout_clicked()
{
    //停止监听
    on_btnListen_clicked();
    //释放服务器对象 server,避免内存泄漏
    delete server;
    //关闭窗口
    this->close();
}

给代码添加了详细的注释,有问题欢迎一起讨论。】

3、客户端开发

#include "widget.h"
#include "ui_widget.h"

#include <QTimer>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    //初始化ui界面
    ui->setupUi(this);
    //布局自适应
    this->setLayout(ui->verticalLayout);
    //设置按钮状态
    ui->btndiscon->setEnabled(false);
    ui->btnSend->setEnabled(false);
    //实例化一个QTcpSocket对象
    client = new QTcpSocket(this);
    connect(client,SIGNAL(readyRead()),this,SLOT(mRead_Data_From_Server()));
}

Widget::~Widget()
{
    //析构
    delete ui;
}


void Widget::on_btnConnect_clicked()
{
    //通过ip端口号创建连接
    client->connectToHost(ui->lineEditIPAddr->text(),ui->lineEditPort->text().toInt());
    //实例化一个计时器,五秒没连接成功给提示
    timer = new QTimer(this);
    timer->setInterval(5000);
    connect(timer,SIGNAL(timeout()),this,SLOT(onTimeOut()));
    connect(client,SIGNAL(connected()),this,SLOT(onConnected()));
    connect(client,SIGNAL(QAbstractSocket::SocketError),this,SLOT(onError(QAbstractSocket::SocketError)));
    //连接按钮按下的时候全窗口不可用,等成功连接时设置可用
    this->setEnabled(false);
    //启动定时器
    timer->start();
}

void Widget::mRead_Data_From_Server()
{
    //读取数据
    ui->textEditRev->moveCursor(QTextCursor::End);
    ui->textEditRev->ensureCursorVisible();
    //服务端数据接收之后用黑色字体显示
    mInsertTextByColor(Qt::black,client->readAll());

}

void Widget::on_btnSend_clicked()
{
    //发送数据
    QByteArray sendData = ui->textEditSend->toPlainText().toUtf8();
    client->write(sendData);
    //客户端发送数据之后用红色字体显示
    mInsertTextByColor(Qt::red,sendData);
}

void Widget::on_btndiscon_clicked()
{
    //关闭客户端
    client->close();
    ui->textEditRev->append("终止连接!");
    //设置按钮状态
    ui->btnConnect->setEnabled(true);
    ui->lineEditIPAddr->setEnabled(true);
    ui->lineEditPort->setEnabled(true);
    ui->btndiscon->setEnabled(false);
    ui->btnSend->setEnabled(false);
}

void Widget::onConnected()
{
    //连接成功之后关闭计时器,设置按钮状态
    timer->stop();
    this->setEnabled(true);
    ui->textEditRev->append("连接成功!");
    ui->btnConnect->setEnabled(false);
    ui->lineEditIPAddr->setEnabled(false);
    ui->lineEditPort->setEnabled(false);
    ui->btndiscon->setEnabled(true);
    ui->btnSend->setEnabled(true);
}

void Widget::onError(QAbstractSocket::SocketError error)
{
    qDebug()<<"连接错误!"<<error;
    ui->textEditRev->insertPlainText("连接出问题啦:"+client->errorString());
    this->setEnabled(true);
    on_btndiscon_clicked();
}

void Widget::onTimeOut()
{
    ui->textEditRev->insertPlainText("连接超时");
    client->abort();
    this->setEnabled(true);
}

void Widget::mInsertTextByColor(Qt::GlobalColor color,QString str)
{
    QTextCursor cursor = ui->textEditRev->textCursor();
    QTextCharFormat format;
    format.setForeground(QBrush(QColor(color)));
    cursor.setCharFormat(format);
    cursor.insertText(str);
}

四、结果测试

连接成功

服务端客户端进行通信

服务器停止监听断开连接

客户端断开连接

五、项目总结

类别功能API/方法
TcpServer

监听

bool listen(const QHostAddress& address, quint16 port)
TcpServer连接QTcpSocket*nextPendingConnection()
TcpServer关闭void close()
TcpServer
信号
新连接newConnection()
TcpScoket连接void connectToHost(const QString &host, quint16 port)
TcpScoket发送qint64 write(const QByteArray& data)
TcpScoket接收QByteArray readAll()
TcpScoket断开void disconnectFromHost()

TcpScoket

信号

连接

成功

connected()

TcpScoket

信号

断开

连接

disconnected()

TcpScoket

信号

有可读数据

readyRead()

TcpScoket

信号

成功写入数据bytesWritten(qint64 bytes)
EditText读数据String getText()
EditText写数据void setText(String text)
EditText改变颜色void changeTextColor(int
start, int end, int color)


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

相关文章:

  • 手机扫描仪 含PDF转word功能+OCR识别110种语言
  • 01_JavaScript
  • 深度学习之防止过拟合
  • 307 Temporary Redirect 解决办法 httpx
  • nebula graph传统使用Docker进行项目发版
  • 【笔记分享】NCS/Zephyr USB MSC实现方法介绍
  • 使用 Python 和 python-pptx 构建 Markdown 到 PowerPoint 转换器
  • 153页PPT华为市场管理全景:战略、逻辑、阶段与工具深度解析
  • 从扩展黎曼泽塔函数构造物质和时空的结构-3
  • 高性能缓存:使用 Redis 和本地内存缓存实战示例
  • Altium Designer数模电学习笔记
  • 常用数据库远程连接工具全解析:从入门到高效管理
  • 基于springboot的校园资料分享平台(048)
  • 设计模式之工厂模式的优缺点
  • Qt中通过QLabel实时显示图像
  • 【AI大模型】搭建本地大模型GPT-NeoX:详细步骤及常见问题处理
  • AI与自媒体的深度融合路径、场景与挑战
  • opencascade 源码学习 XmlDrivers-XmlDrivers
  • 微信 MMTLS 协议详解(五):加密实现
  • Vue.js 中的 Tree Shaking:优化你的应用性能