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

Qt/C++开源项目 TCP客户端调试助手(源码分享+发布链接下载)

这是一个TCP客户端调试助手,具有简洁直观的界面,用户能够方便地测试TCP协议的通信功能,并可同时作为客户端与服务器端使用。以下是该程序的功能特点及用途介绍:

功能特点:

  1. TCP客户端与服务器调试:支持同时作为TCP客户端和服务器端使用,方便进行本地和远程通信的调试。
  2. 连接状态监控:实时显示客户端和服务器的连接状态,帮助用户快速了解连接是否成功。
  3. 发送与接收数据:支持手动输入数据进行发送,能够清晰显示发送和接收的数据,帮助用户观察通信效果。
  4. 自动定时发送:具有自动发送功能,用户可以根据需要设置自动发送的时间间隔,用于测试持续发送的情况。
  5. HEX显示模式:提供十六进制模式显示发送和接收的数据,方便用户调试协议中的字节流。
  6. 多条定时发送指令:可预设多条指令进行定时发送,适合需要同时发送多组数据的场景。
  7. 清空缓存功能:提供一键清空接收缓存和发送缓存的功能,确保数据流的清晰可见性。
  8. 提示信息展示:通过不同颜色的提示信息,提醒用户连接状态、发送接收成功或失败等,便于迅速做出判断。

用途:

  • 网络通信调试:用于调试TCP/IP协议下的网络通信,适合网络设备开发、网络应用开发人员使用。
  • 协议验证:能够帮助验证通信协议的正确性,尤其是需要手动发送测试指令的场景。
  • 服务器负载测试:通过设置多条自动发送指令,可以用于初步测试服务器的负载能力。
  • 学习和测试工具:对网络编程和通信协议学习者来说,是一个很好的入门工具,可以直观地理解TCP通信机制。

下载链接:

通过百度网盘分享的文件:TCP调试助手.zip
链接:https://pan.baidu.com/s/1ItOuy6e8XgN1jw7lt-WYwA?pwd=cedu 
提取码:cedu

源码分享

#ifndef TCPCLIENTTHREAD_H
#define TCPCLIENTTHREAD_H

#include <QThread>
#include <QTcpSocket>
#include <QMutex>
#include <QQueue>

#define tc(a) QString::fromLocal8Bit(a)

class TcpClientThread : public QThread
{
    Q_OBJECT

public:
    explicit TcpClientThread(QObject *parent = nullptr);
    ~TcpClientThread();

    void setServerInfo(const QString &host, int port);  // 设置服务器地址和端口
    void sendData(const QByteArray &data);              // 发送数据
    void stop();                                        // 停止线程

signals:
    void clientConnected();     // 客户端连接信号
    void clientDisconnected();  // 断开连接信号
    void errors(int index, const QString &msg);         // 错误信号
    void warnings(int index, const QString &msg);       // 警告信号
    void informations(int index, const QString &msg);   // 信息信号
    void ClientInfor(const int flag, const QByteArray &msg); // 接收到的信息信号

protected:
    void run() override;  // 重写线程的 run() 函数

private slots:
    void onReadyRead();      // 处理数据读取
    void onDisconnected();   // 处理断开连接

private:
    QTcpSocket *tcpSocket;   // TCP 套接字
    QString host;            // 服务器地址
    int port;                // 服务器端口
    QMutex mutex;            // 互斥锁,用于保护缓冲区
    QQueue<QByteArray> sendBuffer; // 发送缓冲队列
    bool m_run;          // 标志线程是否运行
};

#endif // TCPCLIENTTHREAD_H
// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QStandardItemModel>
#include "tcpclientthread.h"

// 定义宏用于中文字符转换
#define tc(a) QString::fromLocal8Bit(a)

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
enum RunTimeStatus
{
    Error,            ///< 错误信息
    Warning,          ///< 警告信息
    Information,     ///< 常规信息
};
class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    void initStyle();



    //创建发送框
    void createSendLinEdit();

private slots:
    void onClientConnected(); // 客户端连接信号槽
    void onClientDisconnected(); // 客户端断开信号槽
    void handleErrors(int index, const QString &msg);      // 处理错误信号槽
    void handleWarnings(int index, const QString &msg);    // 处理警告信号槽
    void handleInformations(int index, const QString &msg); // 处理信息信号槽
    void handlerClientInfor(const int flag,const QByteArray &data);
    void writeRunTimeMsgs(const QString &msg, const int level); // 输出运行时消息


    void on_clearRunTimeutton_clicked();

    void on_clearRecvButton_clicked();

    void on_closeTip_clicked();


    void on_connectedServerButton_clicked();

    void on_disconectedserverButton_clicked();

    void on_newOpenClient_clicked();

    void on_newOpenServer_clicked();

private:
    Ui::MainWindow *ui;
    TcpClientThread *tcpClientThread;  // TCP 客户端线程
};
#endif // MAINWINDOW_H
// mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDateTime>
#include <QTextEdit>
#include <QFile>
#include <QProcess>
#include "timesendwidget.h"
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
    ,tcpClientThread(new TcpClientThread(this))
{
    ui->setupUi(this);


    // 连接 TcpClientThread 的信号到 MainWindow 的槽
    connect(tcpClientThread, &TcpClientThread::clientConnected, this, &MainWindow::onClientConnected);
    connect(tcpClientThread, &TcpClientThread::clientDisconnected, this, &MainWindow::onClientDisconnected);
    connect(tcpClientThread, &TcpClientThread::errors, this, &MainWindow::handleErrors);
    connect(tcpClientThread, &TcpClientThread::warnings, this, &MainWindow::handleWarnings);
    connect(tcpClientThread, &TcpClientThread::informations, this, &MainWindow::handleInformations);
    connect(tcpClientThread, &TcpClientThread::ClientInfor, this, &MainWindow::handlerClientInfor);


    createSendLinEdit();
    initStyle();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::initStyle()
{
    //加载样式表
    QString qss;
    QFile file(":/qss/psblack.css");
    if (file.open(QFile::ReadOnly)) {
#if 1
        //用QTextStream读取样式文件不用区分文件编码 带bom也行
        QStringList list;
        QTextStream in(&file);
        //in.setCodec("utf-8");
        while (!in.atEnd()) {
            QString line;
            in >> line;
            list << line;
        }

        qss = list.join("\n");
#else
        //用readAll读取默认支持的是ANSI格式,如果不小心用creator打开编辑过了很可能打不开
        qss = QLatin1String(file.readAll());
#endif
        QString paletteColor = qss.mid(20, 7);
        qApp->setPalette(QPalette(paletteColor));
        qApp->setStyleSheet(qss);
        file.close();
    }




}

void MainWindow::createSendLinEdit()
{
    for(int i=0;i<10;i++)
    {
        TimeSendWidget *sendWidget=new TimeSendWidget;
        connect(sendWidget,&TimeSendWidget::sendLineData,[=](const QByteArray &data )
        {
            if(data.isEmpty())
            {
                writeRunTimeMsgs(tc("信息为空,拒绝发送"),Warning);
                return ;
            }


            if(tcpClientThread)
                tcpClientThread->sendData(data);


        });
        ui->verticalLayout->addWidget(sendWidget);
    }
}




// 处理客户端连接的槽函数
void MainWindow::onClientConnected()
{
    writeRunTimeMsgs(tc("客户端连接成功") , 2);
    ui->connectedServerButton->setEnabled(false);
}

// 处理客户端断开的槽函数
void MainWindow::onClientDisconnected()
{
    writeRunTimeMsgs(tc("客户端断开连接"), 1);
    ui->connectedServerButton->setEnabled(true);
}

// 处理错误信号
void MainWindow::handleErrors(int index, const QString &msg)
{
    Q_UNUSED(index);
    writeRunTimeMsgs(msg, Error);
}

// 处理警告信号
void MainWindow::handleWarnings(int index, const QString &msg)
{
    Q_UNUSED(index);
    writeRunTimeMsgs( msg, Warning);
}

// 处理信息信号
void MainWindow::handleInformations(int index, const QString &msg)
{
    Q_UNUSED(index);
    writeRunTimeMsgs(msg, Information);
}

void MainWindow::handlerClientInfor(const int flag, const QByteArray &data)
{

    QString prefix;
    QString color;
    QString msg=tc("%1客户端: %3").arg(flag>0?tc("接收←"):tc("发送→")).arg(ui->isShowHexButton->isChecked()? data.toHex(' ').toUpper():QString::fromLocal8Bit(data));

    if(flag>0)
    {

        //更新显示信息
        ui->recvByte->setValue(ui->recvByte->value()+data.size());
        ui->recvFram->setValue(ui->recvFram->value()+1);

        if(!ui->isShowRecvButton->isChecked())
            return;
        prefix = tc("【接收】");
        color = "#00ff00";
    }
    else
    {

        ui->sendByte->setValue(ui->sendByte->value()+data.size());
        ui->sendFram->setValue(ui->sendFram->value()+1);

        if(!ui->isShowSendButton->isChecked())
            return;

        prefix = tc("【发送】");
        color = "orange";


    }


    // 获取当前时间
    QString timestamp = ui->isShowTimeButton->isChecked()?QDateTime::currentDateTime().toString("hh:mm:ss(zzz)"):"";
    // 将消息插入到QTextEdit中并改变颜色
    // 将消息插入到QTextEdit中并改变颜色
    QString formattedMsg = QString("<span style='color:%1;'>%2 %3: %4</span>").arg(color, prefix, timestamp,msg);
    ui->receiveTextEdit->append(formattedMsg);
}

// 输出运行时消息
void MainWindow::writeRunTimeMsgs(const QString &msg, const int level)
{
    QString prefix;
    QString color;

    // 获取当前时间
    QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");

    switch (level) {
    case 0: // 异常
        prefix = tc("【异常】");
        color = "red";
        break;
    case 1: // 警报
        prefix = tc("【警报】");
        color = "orange";
        break;
    case 2: // 提示
        prefix = tc("【提示】");
        color = "#00ff00";
        break;
    }
    // 将消息插入到QTextEdit中并改变颜色
    QString formattedMsg = QString("<span style='color:%1;'>%2 %3: %4</span>").arg(color, prefix, timestamp, msg);
    ui->outputTextEdit->append(formattedMsg);

}




void MainWindow::on_clearRunTimeutton_clicked()
{
    ui->outputTextEdit->clear();
}

void MainWindow::on_clearRecvButton_clicked()
{
    ui->receiveTextEdit->clear();
}

void MainWindow::on_closeTip_clicked()
{
    ui->groupBox->setVisible(!ui->groupBox->isVisible());
}



void MainWindow::on_connectedServerButton_clicked()
{
    QString host = ui->hostLineEdit->text();
    int port = ui->portspinBox->text().toInt();

    if (!tcpClientThread->isRunning())
    {
        tcpClientThread->setServerInfo(host, port);
        tcpClientThread->start();  // 启动线程
    } else
    {
        // QMessageBox::warning(this, tc("警告"), tc("已经连接到服务器"));
    }
}

void MainWindow::on_disconectedserverButton_clicked()
{
    if(tcpClientThread)
        tcpClientThread->stop();
}

void MainWindow::on_newOpenClient_clicked()
{
    if (!QProcess::startDetached(QCoreApplication::applicationFilePath())) {
        writeRunTimeMsgs(tc("新客户端启动失败!"),Error);
    } else {
        writeRunTimeMsgs(tc("新客户端启动成功!"),Information);
    }

}

void MainWindow::on_newOpenServer_clicked()
{
    if (!QProcess::startDetached("QTcpServerDemo.exe")) {
        writeRunTimeMsgs(tc("新服务器启动失败!"),Error);
    } else {
        writeRunTimeMsgs(tc("新服务器启动成功!"),Information);
    }
}
#include "tcpclientthread.h"
#include <QHostAddress>
#include <QEventLoop>

#define tc(a) QString::fromLocal8Bit(a)

TcpClientThread::TcpClientThread(QObject *parent)
    : QThread(parent), tcpSocket(nullptr), m_run(true)
{
}

TcpClientThread::~TcpClientThread()
{
    stop();  // 停止线程
    wait();  // 等待线程结束
}

void TcpClientThread::setServerInfo(const QString &host, int port)
{
    this->host = host;
    this->port = port;
}

void TcpClientThread::run()
{

    tcpSocket = new QTcpSocket();
    connect(tcpSocket, &QTcpSocket::readyRead, this, &TcpClientThread::onReadyRead);
    connect(tcpSocket, &QTcpSocket::disconnected, this, &TcpClientThread::onDisconnected);

    // 连接到服务器
    tcpSocket->connectToHost(QHostAddress(host), port);

    if (tcpSocket->waitForConnected(3000))
    {
        emit clientConnected();
        emit informations(0, tc("已成功连接到服务器"));
    } else {
        emit errors(0, tc("连接服务器失败"));
        return;
    }

    m_run = true;
    QEventLoop eventLoop;  // 创建局部事件循环

    // 主循环,处理事件和发送数据
    while (m_run) {
        // 处理事件,防止阻塞信号槽
        eventLoop.processEvents(QEventLoop::AllEvents, 50);

        // 检查发送队列
        QMutexLocker locker(&mutex);
        if (!sendBuffer.isEmpty())
        {
            QByteArray dataToSend = sendBuffer.dequeue();
            if (tcpSocket->write(dataToSend) == -1)
            {
                emit errors(1, tc("发送数据失败"));
            } else
            {
                emit ClientInfor(0,dataToSend);
            }
        }

        msleep(10);  // 避免占用过多CPU
    }

    // 断开连接
    if (tcpSocket->state() == QAbstractSocket::ConnectedState) {
        tcpSocket->disconnectFromHost();
        if (tcpSocket->state() != QAbstractSocket::UnconnectedState) {
            tcpSocket->waitForDisconnected(3000);
        }
    }

    tcpSocket->deleteLater();
}

void TcpClientThread::stop()
{
    m_run = false;
}

void TcpClientThread::sendData(const QByteArray &data)
{
    QMutexLocker locker(&mutex);  // 加锁保护
    sendBuffer.enqueue(data);  // 添加到发送队列
}

void TcpClientThread::onReadyRead()
{
    QByteArray data = tcpSocket->readAll();
    emit ClientInfor(1, data);  // 发射接收到的数据信号
}

void TcpClientThread::onDisconnected()
{
    emit clientDisconnected();
}


http://www.kler.cn/news/295007.html

相关文章:

  • <class ‘pyspark.sql.dataframe.DataFrame‘>
  • Eureka原理与实践:构建高可用微服务架构的基石
  • MCU5.51单片机的最小系统
  • IDEA git提交时如何忽略某个文件或文件夹
  • 任务执行拓扑排序(华为od机考题)
  • Elasticsearch - SpringBoot 索引与文档相关demo
  • Spring Boot 部署方案!打包 + Shell 脚本详解
  • 【知识分享】MQTT实战-使用mosquitto客户端连接emqx服务器
  • 【人工智能】Transformers之Pipeline(十五):总结(summarization)
  • ubuntu上通过openvswitch卸载实现roce over vxlan
  • 橘子学ES实战操作之管道类型Ingest pipelines的基本使用
  • Kubernetes 1.25 containerd 环境部署 SuperMap iManager
  • 【MRI基础】TR 和 TE 时间概念
  • 文心快码前端工程师观点分享:人机协同新模式的探索之路(三)
  • day44-测试平台搭建之前端vue学习-基础4
  • java-redis-雪崩
  • QString如何格式化字符串
  • 3.门锁_STM32_矩阵按键设备实现
  • 手机同时传输USB功能与充电的实现及LDR6500的作用
  • Java爬虫开发:Jsoup库在图片URL提取中的实战应用
  • 使用Node-API进行线程安全开发
  • 枚举和联合体
  • 在生产线打包机中RFID技术的赋能
  • Vue3.5正式发布带来了那些新特性?
  • MMO地图传送
  • 计网名词解释
  • 【qt踩坑】路径含中文导致的报错,以及 OpenGL的链接报错
  • STM32学习笔记4 --- USART
  • 实现点击 `el-dialog` 里面的一个图标将对话框放大至全屏
  • QT自动获取编译日期与git commit ID