这是一个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_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();
}