目录
一、前言
二、环境准备以及项目创建
三、 项目实现
1.ui界面设计
2.添加NetWork模块
QTcpSocket 和 QTcpServer
QUdpSocket
3.主要功能实现
①IP扫描
②端口设置
③数据接收
④数据发送
⑤日志保存
4.打包成exe
四、效果展示
五、总结
一、前言
我之前用基于pyqt5一共实现过俩款小工具,分别是串口助手以及网络调试助手。文章如下:
基于Python+Pycharm+PyQt5的串口助手开发_基于python+pyqt5的串口助手-CSDN博客
基于PyQt5的网络调试助手开发_pyqt5做网络调试助手-CSDN博客
但是前不久呢,开始接触了QT和C++之后。我就把串口助手基于C++重写实现了一版.基于VS2022+Qt5+C++的串口助手开发_vs2022 c++串口在哪儿-CSDN博客文章浏览阅读1.5k次,点赞20次,收藏18次。如果有人之前看过我文章的话应该知道,我之前用python+pyqt5写过一版串口助手。没看过的也不要紧,我贴在下面,大家可以浅浅看一下。最近由于工作需要,开始接触C++以及QT,就想着能不能用C++和QT重写一下串口助手。于是就有了这篇文章单纯记录一下,接下来听我娓娓道来~基于Python+Pycharm+PyQt5的串口助手开发_基于python+pyqt5的串口助手-CSDN博客本期带来的基于PyQt5的串口助手开发,实现串口通信。_基于python+pyqt5的串口助手。_vs2022 c++串口在哪儿https://blog.csdn.net/weixin_44765053/article/details/141184173?spm=1001.2014.3001.5502 同样地,今天带来的文章是网络调试助手开发,基于QT和C++重写实现。
二、环境准备以及项目创建
操作系统:win 10
编辑器:VS2022、QT5.14
语言及版本:C++
最终实现的功能:UDP通信、TCP客户端/服务端通信、发送数据、接收数据、数据显示
具体的VS配置QT以及项目创建过程我这边就不再赘述,有不知道的小伙伴可以串口那边文章。
三、 项目实现
1.ui界面设计
继续偷个小懒,直接沿用pyqt网络调试助手项目的ui设计。
大致包括:
2个textEdit,分别是发送框和接受框;
2个comboBox和一个lineEdit,表示协议类型、IP以及端口;
5个checkBox,表示发送、接收格式等;
若干个pushButton;
......
ps:这边我较pyqt项目新增了一个保存日志开关,不知道是否有眼尖的小伙伴发现呢
2.添加NetWork模块
Qt的网络模块(QtNetwork
)提供了一套用于开发网络应用程序的工具,它支持多种网络协议,使得开发者可以轻松地创建客户端或服务器端的应用程序。这里我们主要用到的是QTcpSocket
、QTcpServer以及QUdpSocket
QTcpSocket
和 QTcpServer
QTcpSocket
:封装了 TCP 客户端的功能,用于与服务器建立 TCP 连接。它提供了异步的读写接口,支持流式数据传输。QTcpServer
:用于创建 TCP 服务器,监听特定的端口并接受来自客户端的连接。每个新连接都会产生一个QTcpSocket
对象,用于与客户端通信。
QUdpSocket
- 功能:提供 UDP 通信功能,适合无连接的网络传输场景。支持广播和组播,适合实时通信或局域网内的应用。
- 特点:
- 无连接:不需要建立连接,数据直接发送到指定 IP 和端口。
- 快速:适合对延时敏感的应用,但不保证数据的可靠传输。
那么要怎么样才能在我们的项目中使用NetWork模块呢?
依次点击,项目-->NetAssistant和属性-->Qt Project Settings-->Qt Modules-->添加network。
但是,往往这样只是这样还不够,会报错显示无法打开源文件QtNetWork,这时候就需要在项目--->属性---->配置属性---->C/C++---->常规---->附加包含目录”添加:
$(QTDIR)\include\QtNetwork
3.主要功能实现
主要功能包括:
- IP扫描:定时每秒扫描网卡IPV4,并将其添加到ComboBox。
- 端口设置:根据通信协议设置绑定IP、端口。
- 数据接收:接收数据,并以十六进制或者正常显示。
- 数据发送:包括十六进制发送、定时发送数据等待。
- 数据保存:将数据日志保存到log文件
①IP扫描
QNetworkInterface
,主要用于获取本地设备的网络接口信息。通过它,可以枚举系统中所有的网络接口,获取接口的名称、类型、IP 地址等相关信息。
QStringList NetAssistant::GetIp()
{
// 获取系统中所有网络接口的IP地址列表
QList <QHostAddress> ip_list = QNetworkInterface::allAddresses();
QStringList scan_ip; // 初始化一个空的 QStringList,用于存储扫描到的有效IP地址
for (const QHostAddress& ip : ip_list) {
if (ip.isNull()) // 忽略空的IP地址
continue;
// 获取IP地址的协议类型
QAbstractSocket::NetworkLayerProtocol n_protocol = ip.protocol();
// 如果协议类型为IPv4,则将IP地址添加到有效IP地址列表中
if (n_protocol == QAbstractSocket::IPv4Protocol) {
scan_ip.append(ip.toString());
}
}
return scan_ip; // 返回有效IP地址列表
}
②端口设置
对于UDP。QUdpSocket::bind
是 QUdpSocket
类中的一个重要方法,用于将 UDP 套接字(socket)绑定到指定的地址和端口,从而让套接字开始监听并接收来自该端口的数据。
#include <QUdpSocket>
#include <QHostAddress>
#include <QDebug>
int main() {
QUdpSocket udpSocket;
// 绑定到任意地址的 1234 端口
if (udpSocket.bind(QHostAddress::AnyIPv4, 1234)) {
qDebug() << "UDP socket bound to port 1234.";
} else {
qDebug() << "Failed to bind the UDP socket.";
}
return 0;
}
对于TCP客户端。 connectToHost
是 QTcpSocket
类中的一个方法,用于建立 TCP 客户端与服务器之间的连接。它尝试将客户端套接字连接到指定的服务器 IP 地址和端口号。连接成功后,客户端可以通过该套接字与服务器进行数据通信。
#include <QTcpSocket>
#include <QHostAddress>
#include <QDebug>
int main() {
QTcpSocket tcpClientSocket;
// 尝试连接到本地服务器的 1234 端口
tcpClientSocket.connectToHost(QHostAddress::LocalHost, 1234);
// 等待连接完成
if (tcpClientSocket.waitForConnected(3000)) { // 等待 3 秒
qDebug() << "Connected to server!";
} else {
qDebug() << "Failed to connect to server.";
}
return 0;
}
对于TCP服务端。listen
是 QTcpServer
类中的一个方法,用于让服务器开始监听指定的地址和端口,等待客户端的连接请求。
#include <QTcpServer>
#include <QHostAddress>
#include <QDebug>
int main() {
QTcpServer tcpServerSocket;
// 监听本机所有可用网络接口的 1234 端口
if (tcpServerSocket.listen(QHostAddress::Any, 1234)) {
qDebug() << "Server is listening on port 1234.";
} else {
qDebug() << "Failed to start server.";
}
return 0;
}
③数据接收
对于UDP。hasPendingDatagrams()
是 Qt 中 QUdpSocket
类的一个成员函数,用于检查是否有未处理的数据报等待读取。
#include <QTcpServer>
#include <QUdpSocket>
class MyUDPReceiver : public QObject {
Q_OBJECT
public:
explicit MyUDPReceiver(QObject *parent = nullptr) : QObject(parent), udpSocket(new QUdpSocket(this)) {
udpSocket->bind(1234); // 绑定到端口1234
connect(udpSocket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
}
public slots:
void readPendingDatagrams() {
while (udpSocket->hasPendingDatagrams()) {
QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize());
QHostAddress sender;
quint16 senderPort;
udpSocket->readDatagram(datagram.data(), datagram.size(),
&sender, &senderPort);
qDebug() << "Received datagram:" << datagram
<< "from" << sender.toString() << "port" << senderPort;
}
}
private:
QUdpSocket *udpSocket;
};
对于TCP客户端。readAll()
是 Qt 中 QTcpSocket
类的一个成员函数,用于从套接字中读取所有可用的数据 。
#include <QTcpSocket>
class TcpClient : public QObject {
Q_OBJECT
public:
explicit TcpClient(QObject *parent = nullptr) : QObject(parent), tcpSocket(new QTcpSocket(this)) {
tcpSocket->connectToHost("example.com", 1234);
connect(tcpSocket, SIGNAL(connected()), this, SLOT(onConnected()));
connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
}
public slots:
void onConnected() {
if (tcpSocket->state() == QTcpSocket::ConnectedState) {
qDebug() << "Connected to server.";
}
}
void onReadyRead() {
QByteArray data = tcpSocket->readAll();
if (!data.isEmpty()) {
qDebug() << "Received data:" << data;
}
}
private:
QTcpSocket *tcpSocket;
};
对于TCP服务端。bytesAvailable()
是 Qt 中 QAbstractSocket
类的一个成员函数,用于获取当前套接字中可用的数据字节数量
#include <QTcpServer>
#include <QTcpSocket>
class TcpServer : public QObject {
Q_OBJECT
public:
explicit TcpServer(QObject *parent = nullptr) : QObject(parent), server(new QTcpServer(this)) {
if (server->listen(QHostAddress::Any, 12345)) {
qDebug() << "Server is listening on port 12345";
} else {
qDebug() << "Failed to start the server.";
}
connect(server, SIGNAL(newConnection()), this, SLOT(onNewConnection()));
}
public slots:
void onNewConnection() {
QTcpSocket *client = server->nextPendingConnection();
if (client) {
qDebug() << "New client connected:" << client->peerAddress().toString();
connect(client, SIGNAL(readyRead()), this, SLOT(onClientReadyRead(client)));
}
}
void onClientReadyRead(QTcpSocket *client) {
while (client->bytesAvailable() > 0) {
QByteArray data = client->read(client->bytesAvailable());
qDebug() << "Received from client:" << data;
// 处理接收到的数据
}
}
private:
QTcpServer *server;
};
④数据发送
对于UDP。writeDatagram()
是 Qt 中 QUdpSocket
类的一个成员函数,用于向指定的目标地址和端口发送一个 UDP 数据报。
#include <QUdpSocket>
#include <QHostAddress>
class UdpSender : public QObject {
Q_OBJECT
public:
explicit UdpSender(QObject *parent = nullptr) : QObject(parent), udpSocket(new QUdpSocket(this)) {}
void sendDatagram() {
QByteArray datagram = "Hello, UDP!";
QHostAddress targetAddress("192.168.1.2"); // 目标地址
quint16 targetPort = 12345; // 目标端口
qint64 bytesWritten = udpSocket->writeDatagram(datagram, targetAddress, targetPort);
if (bytesWritten == datagram.size()) {
qDebug() << "Datagram sent successfully.";
} else {
qDebug() << "Failed to send datagram.";
}
}
private:
QUdpSocket *udpSocket;
};
对于TCP客户端、服务端。write()
是 Qt 中 QTcpSocket
类的一个成员函数,用于向 TCP 连接的另一端发送数据。这个函数在 TCP 客户端和服务器端都可以使用
#include <QTcpSocket>
#include <QCoreApplication>
#include <QDebug>
class TcpClient : public QObject {
Q_OBJECT
public:
explicit TcpClient(QObject *parent = nullptr) : QObject(parent), tcpSocket(new QTcpSocket(this)) {
tcpSocket->connectToHost("example.com", 12345);
connect(tcpSocket, SIGNAL(connected()), this, SLOT(onConnected()));
connect(tcpSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), this, SLOT(onError(QAbstractSocket::SocketError)));
}
public slots:
void onConnected() {
if (tcpSocket->state() == QTcpSocket::ConnectedState) {
qDebug() << "Connected to server.";
QByteArray data = "Hello, Server!";
qint64 bytesWritten = tcpSocket->write(data);
if (bytesWritten == data.size()) {
qDebug() << "Data sent successfully.";
} else {
qDebug() << "Failed to send data.";
}
}
}
void onError(QAbstractSocket::SocketError socketError) {
qDebug() << "Socket error occurred:" << tcpSocket->errorString();
}
private:
QTcpSocket *tcpSocket;
};
⑤日志保存
如果不存在logs文件夹则生成,以当天时间命名log日志文件。
void NetAssistant::save_log(QString text)
{
QString log_path = "logs";
QDir dir;
if (!dir.exists(log_path))
{
bool res = dir.mkpath(log_path);
if (res != true)
{
QMessageBox::warning(this, "错误", "log 文件夹创建失败");
return;
}
}
QString filename = log_path + "\\" + QDateTime::currentDateTime().toString("yyyy-MM-dd") + ".log";
QFile file(filename);
if (!file.open(QIODevice::WriteOnly | QIODevice::Append)) {
// 处理错误,例如弹出对话框通知用户
return;
}
QTextStream textStream(&file);
// 写入文本内容
textStream << text << endl;
// 关闭文件
file.close();
}
4.打包成exe
打开项目使用的编译器,然后cd到项目的release目录。使用一下命令打包成可执行文件
windeployqt xxx.exe
如下所示,所有项目相关的dll文件都被移到当前文件下
四、效果展示
qt网络调试助手
五、总结
通过本文的详细讲解,我们完成了一个基于VS2022和Qt5的网络调试助手的开发。包括UDP、TCP等通信、收发数据、保存日志等功能。
在开发过程中,我们深入了解了QtNetWork的应用,通过它轻松实现了网络通信。同时,本文还详细介绍了如何在Qt中设计用户界面,使我们的工具不仅功能完善,而且使用起来简洁直观。
希望通过这篇文章,你能够更好地掌握Qt开发知识,并将这些技能应用到实际项目中。如果你在开发过程中遇到任何问题,欢迎在评论区交流讨论。