该TCP服务器调试助手是用于测试和监控基于TCP协议的网络通信工具,能够帮助开发者便捷地进行网络通信调试。通过简洁的界面设计,用户可以轻松配置、管理TCP端口的连接,收发消息并进行数据监控分析。以下是该工具的主要功能:
1. 端口管理
- 端口设置 :用户可以在界面上设置服务器监听的端口号。在当前截图中,端口号被设置为
123
。 - 打开/关闭端口:用户可以通过点击"打开"或"关闭"按钮来启动或关闭指定端口。程序将监听该端口的TCP连接请求,并提供状态反馈。
- 清空信息:通过"清空信息"和"清空接收"按钮,用户可以快速清除当前界面上显示的日志信息和接收到的消息,方便调试过程中清理数据。
2. 数据交互
- 消息显示 :程序可以显示详细的TCP通信记录,包括消息的发送时间 、消息内容 以及发送状态 (发送成功或接收成功)。界面上区分了发送和接收消息,发送消息显示为橙色,接收消息显示为绿色。
- 通信消息格式:用户可选择以文本或HEX格式查看接收到的数据,并且可以通过界面勾选切换两种显示模式(例如:HEX显示和文本显示)。
- 收发统计:程序提供了"收到帧数"、"发送帧数"、"收到字节"、"发送字节"等统计信息,帮助用户实时监控通信的数据量。
3. 群发功能
- 定时群发 :程序允许用户设置定时群发功能,用户可以向多个客户端定时发送消息。通过配置每个群发框,用户可以分别设置群发内容 及时间间隔 (以毫秒为单位)。截图中默认时间间隔为
1000
毫秒,即每秒发送一次。 - HEX发送选项:在发送数据时,用户可以选择是否将消息以HEX格式发送,灵活满足不同的通信需求。
4. 消息提示
- 程序下方的提示窗口会显示实时的运行状态,例如:
- 成功启动服务器并监听端口的提示;
- 新客户端的连接信息(包含连接时间及客户端ID)。
- 这些提示信息帮助用户快速了解当前通信状态并排查可能存在的连接问题。
5. 客户端连接管理
- 连接显示 :该工具不仅可以接收来自多个客户端的数据,还能显示每个客户端的唯一标识,如"客户端#1364"或"客户端#1380",帮助用户有效管理多个连接。
6. 高度自定义
- 动态配置发送消息:用户可以在程序界面中动态输入或修改即将发送的消息内容,并且在必要时可通过"清空"按钮快速重置待发送的消息。
- 多消息通道:提供了多个消息发送输入框,方便用户针对不同的客户端或场景进行快速的消息群发操作。
发布软件下载链接
通过百度网盘分享的文件:TCP服务器调试助手.zip
链接:https://pan.baidu.com/s/1UF_fEztbQnk2UIJJUR7s3g?pwd=cedu
提取码:cedu
cpp
// tcpserver.h
#ifndef TCPSERVER_H
#define TCPSERVER_H
#include <QThread>
#include <QTcpServer>
#include <QTcpSocket>
#include <QMap>
#define tc(a) QString::fromLocal8Bit(a)
class TcpServer : public QThread
{
Q_OBJECT
public:
explicit TcpServer(QObject *parent = nullptr);
void run() override; // 线程入口点
bool startServer(int port); // 启动服务器
void closeServer();
void sendToAllClients(const QByteArray &message); // 群发消息
void sendToClient(qintptr socketDescriptor, const QByteArray &message); // 发送消息给指定客户端
signals:
void clientConnected(qintptr socketDescriptor); // 新客户端连接信号
void clientDisconnected(qintptr socketDescriptor); // 客户端断开信号
void errors(int index, const QString &msg); // 错误信号
void warnings(int index, const QString &msg); // 警告信号
void informations(int index, const QString &msg); // 信息信号
void ClientInfor(const long long socketDescriptor,const QByteArray &msg,const bool isRecv) ; ///<接收客户端信息
private slots:
void onNewConnection(); // 新客户端连接时触发
void onClientDisconnected(); // 客户端断开时触发
void onReadyRead(); // 客户端有数据时触发
private:
QTcpServer *tcpServer; // TCP服务器
QMap<qintptr, QTcpSocket*> clients; // 保存客户端列表
};
#endif // TCPSERVER_H
cpp
// tcpserver.cpp
#include "tcpserver.h"
#include <QHostAddress>
#include <QDebug>
TcpServer::TcpServer(QObject *parent)
: QThread(parent), tcpServer(nullptr)
{
}
void TcpServer::run()
{
exec(); // 启动线程事件循环
}
// 启动服务器并监听指定端口
bool TcpServer::startServer(int port)
{
tcpServer = new QTcpServer();
if (!tcpServer->listen(QHostAddress::Any, static_cast<quint16>(port)))
{
emit errors(1, tc("服务器启动失败:%1 端口: ").arg(tcpServer->errorString()) + QString::number(port));
return false;
}
connect(tcpServer, &QTcpServer::newConnection, this, &TcpServer::onNewConnection);
emit informations(1, tc("服务器启动成功,监听端口: ") + QString::number(port));
return true;
}
void TcpServer::closeServer()
{
tcpServer->close();
}
// 当有新客户端连接时
void TcpServer::onNewConnection()
{
QTcpSocket *clientSocket = tcpServer->nextPendingConnection();
qintptr socketDescriptor = clientSocket->socketDescriptor();
clients.insert(socketDescriptor, clientSocket); // 添加到客户端列表
connect(clientSocket, &QTcpSocket::disconnected, this, &TcpServer::onClientDisconnected);
connect(clientSocket, &QTcpSocket::readyRead, this, &TcpServer::onReadyRead);
emit clientConnected(socketDescriptor); // 触发客户端连接信号
emit informations(2, tc("新的客户端连接,描述符: ") + QString::number(socketDescriptor));
}
// 当客户端断开连接时
void TcpServer::onClientDisconnected()
{
QTcpSocket *clientSocket = qobject_cast<QTcpSocket*>(sender());
if (clientSocket)
{
qintptr socketDescriptor=0;
for (auto key : clients.keys()) {
if(clients[key]==clientSocket)
socketDescriptor = key;
}
clients.remove(socketDescriptor); // 从客户端列表中移除
emit clientDisconnected(socketDescriptor); // 触发客户端断开信号
emit warnings(1, tc("客户端断开连接,描述符: ") + QString::number(socketDescriptor));
clientSocket->deleteLater();
}
}
// 当有客户端发送数据时
void TcpServer::onReadyRead()
{
QTcpSocket *clientSocket = qobject_cast<QTcpSocket*>(sender());
if (clientSocket) {
QByteArray data = clientSocket->readAll();
emit ClientInfor(clientSocket->socketDescriptor(), data,true);
}
}
// 群发消息
void TcpServer::sendToAllClients(const QByteArray &message)
{
if(clients.keys().isEmpty())
{
emit errors(1, tc("未存在可发送的客户端 "));
return;
}
for(auto key:clients.keys())
sendToClient(key,message);
}
// 发送消息给指定客户端
void TcpServer::sendToClient(qintptr socketDescriptor, const QByteArray &message)
{
QTcpSocket *clientSocket = clients.value(socketDescriptor, nullptr);
if (clientSocket)
{
clientSocket->write(message);
emit ClientInfor(clientSocket->socketDescriptor(), message,false);
} else {
emit errors(2, tc("无法找到指定的客户端,描述符: ") + QString::number(socketDescriptor));
}
}
cpp
// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QStandardItemModel>
#include "tcpserver.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 on_startServerButton_clicked(); // 点击启动服务器按钮
void on_stopserverButton_clicked();
void onClientConnected(qintptr socketDescriptor); // 客户端连接信号槽
void onClientDisconnected(qintptr socketDescriptor); // 客户端断开信号槽
void handleErrors(int index, const QString &msg); // 处理错误信号槽
void handleWarnings(int index, const QString &msg); // 处理警告信号槽
void handleInformations(int index, const QString &msg); // 处理信息信号槽
void handlerClientInfor(const long long id,const QByteArray &data,const bool isRecv);
void writeRunTimeMsgs(const QString &msg, const int level); // 输出运行时消息
void on_clearRunTimeutton_clicked();
void on_clearRecvButton_clicked();
void on_closeTip_clicked();
private:
Ui::MainWindow *ui;
TcpServer *server; // TCP服务器
QStandardItemModel *model; // 共享数据模型
};
#endif // MAINWINDOW_H
cpp
// mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDateTime>
#include <QTextEdit>
#include <QFile>
#include "timesendwidget.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, server(new TcpServer()) // 初始化服务器
{
ui->setupUi(this);
// 连接服务器信号槽
connect(server, &TcpServer::clientConnected, this, &MainWindow::onClientConnected);
connect(server, &TcpServer::clientDisconnected, this, &MainWindow::onClientDisconnected);
connect(server, &TcpServer::errors, this, &MainWindow::handleErrors);
connect(server, &TcpServer::warnings, this, &MainWindow::handleWarnings);
connect(server, &TcpServer::informations, this, &MainWindow::handleInformations);
connect(server, &TcpServer::ClientInfor, this, &MainWindow::handlerClientInfor);
// 创建共享的数据模型
model = new QStandardItemModel(this);
QStandardItem *item = new QStandardItem(tc("群发"));
item->setData(-1,Qt::UserRole);
model->appendRow(item);
createSendLinEdit();
initStyle();
}
MainWindow::~MainWindow()
{
delete ui;
server->quit(); // 退出服务器线程
server->wait(); // 等待线程结束
delete server;
}
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;
sendWidget->setComBoxModel(model);
connect(sendWidget,&TimeSendWidget::sendLineData,[=](const qintptr socketDescriptor,const QByteArray &data )
{
if(data.isEmpty())
{
writeRunTimeMsgs(tc("信息为空,拒绝发送"),Warning);
return ;
}
((socketDescriptor<0)? server->sendToAllClients(data): server->sendToClient(socketDescriptor, data));
});
ui->verticalLayout->addWidget(sendWidget);
}
}
// 启动服务器按钮
void MainWindow::on_startServerButton_clicked()
{
int port = ui->portspinBox->value();
if (port > 0) {
if (server->startServer(port))
{
writeRunTimeMsgs(tc("服务器成功启动,监听端口:") + QString::number(port), 2);
ui->startServerButton->setEnabled(false);
} else {
writeRunTimeMsgs(tc("服务器启动失败!"), 0);
}
} else {
writeRunTimeMsgs(tc("无效的端口号!"), 1);
}
}
// 关闭服务器按钮
void MainWindow::on_stopserverButton_clicked()
{
server->closeServer();
ui->startServerButton->setEnabled(true);
}
// 处理客户端连接的槽函数
void MainWindow::onClientConnected(qintptr socketDescriptor)
{
writeRunTimeMsgs(tc("客户端连接,描述符: ") + QString::number(socketDescriptor), 2);
QStandardItem *item = new QStandardItem(tc("客户端#%1").arg(socketDescriptor));
item->setData(socketDescriptor,Qt::UserRole);
model->appendRow(item);
}
// 处理客户端断开的槽函数
void MainWindow::onClientDisconnected(qintptr socketDescriptor)
{
writeRunTimeMsgs(tc("客户端断开连接,描述符: ") + QString::number(socketDescriptor), 1);
// 从客户端列表中移除
for (int i = 0; i < model->rowCount(); ++i)
{
QStandardItem *item = model->item(i);
if (item && item->data(Qt::UserRole).toLongLong() == socketDescriptor)
{
model->removeRow(i); // 删除找到的行
break; // 如果只想删除第一个匹配的项,删除后直接跳出循环
}
}
}
// 处理错误信号
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 long long id, const QByteArray &data, const bool isRecv)
{
QString prefix;
QString color;
QString msg=tc("%1客户端#%2: %3").arg(isRecv?tc("接收←"):tc("发送→")).arg(id).arg(ui->isShowHexButton->isChecked()? data.toHex(' ').toUpper():QString::fromLocal8Bit(data));
if(isRecv)
{
//更新显示信息
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());
}