这是一个TCP客户端调试助手,具有简洁直观的界面,用户能够方便地测试TCP协议的通信功能,并可同时作为客户端与服务器端使用。以下是该程序的功能特点及用途介绍:
功能特点:
- TCP客户端与服务器调试:支持同时作为TCP客户端和服务器端使用,方便进行本地和远程通信的调试。
- 连接状态监控:实时显示客户端和服务器的连接状态,帮助用户快速了解连接是否成功。
- 发送与接收数据:支持手动输入数据进行发送,能够清晰显示发送和接收的数据,帮助用户观察通信效果。
- 自动定时发送:具有自动发送功能,用户可以根据需要设置自动发送的时间间隔,用于测试持续发送的情况。
- HEX显示模式:提供十六进制模式显示发送和接收的数据,方便用户调试协议中的字节流。
- 多条定时发送指令:可预设多条指令进行定时发送,适合需要同时发送多组数据的场景。
- 清空缓存功能:提供一键清空接收缓存和发送缓存的功能,确保数据流的清晰可见性。
- 提示信息展示:通过不同颜色的提示信息,提醒用户连接状态、发送接收成功或失败等,便于迅速做出判断。
用途:
- 网络通信调试:用于调试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_OBJECTpublic: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_OBJECTpublic: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());
#endifQString 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();
}