【QT/C++】Qt网络编程进阶:UDP通信和HTTP请求的基本原理和实际应用(超详细)
- 本文主要详细说明Qt中的UDP通信和HTTP请求的基本原理和实际应用。
(关注不迷路哈!!!)
文章目录
- 【QT/C++】Qt网络编程进阶:UDP通信和HTTP请求的基本原理和实际应用(超详细)
-
- [1. UDP通信编程](#1. UDP通信编程)
-
- [1.1 UDP通信流程](#1.1 UDP通信流程)
- [1.2 QUdpSocket类](#1.2 QUdpSocket类)
- [1.3 完整UDP服务器示例](#1.3 完整UDP服务器示例)
- [2. HTTP请求编程](#2. HTTP请求编程)
-
- [2.1 HTTP协议简介](#2.1 HTTP协议简介)
- [2.2 QNetworkAccessManager类](#2.2 QNetworkAccessManager类)
- [2.3 QNetworkReply类](#2.3 QNetworkReply类)
- [2.4 完整HTTP请求示例](#2.4 完整HTTP请求示例)
- [3. 综合示例:网络工具集](#3. 综合示例:网络工具集)
-
- [3.1 主窗口头文件(networktool.h)](#3.1 主窗口头文件(networktool.h))
- [3.2 主窗口实现(networktool.cpp)](#3.2 主窗口实现(networktool.cpp))
- [3.3 主函数(main.cpp)](#3.3 主函数(main.cpp))
- [3.4 项目文件(networktool.pro)](#3.4 项目文件(networktool.pro))
- 总结
1. UDP通信编程
QUdpSocket Class 类
| Header | QUdpSocket 头文件 |
|---|---|
| qmake | QT += network 模块 |
| Inherits | QAbstractSocket 父类 |
1.1 UDP通信流程

UDP(User Datagram Protocol)是一种无连接的传输协议,通信流程相对简单:
- 发送方直接发送数据报文到指定地址和端口
- 接收方绑定到特定端口等待接收数据
- 数据报文独立传输,不保证顺序和可靠性
1.2 QUdpSocket类
cpp
#include <QUdpSocket>
#include <QHostAddress>
// 在.pro文件中添加: QT += network
// 构造函数
QUdpSocket *udpSocket = new QUdpSocket(this);
// 发送数据
qint64 bytesSent = udpSocket->writeDatagram("Hello UDP",
QHostAddress("192.168.1.100"),
8080);
// 或者使用QByteArray
QByteArray data = "Hello UDP";
udpSocket->writeDatagram(data, QHostAddress::Broadcast, 8080);
// 接收端绑定端口
udpSocket->bind(QHostAddress::Any, 8080);
// 或绑定特定地址
udpSocket->bind(QHostAddress("192.168.1.100"), 8080);
// 接收数据方式1:使用readDatagram
char buffer[1024];
QHostAddress sender;
quint16 senderPort;
qint64 bytesRead = udpSocket->readDatagram(buffer, sizeof(buffer), &sender, &senderPort);
QString message = QString::fromUtf8(buffer, bytesRead);
// 接收数据方式2:使用QNetworkDatagram(推荐)
while (udpSocket->hasPendingDatagrams()) {
QNetworkDatagram datagram = udpSocket->receiveDatagram();
QByteArray data = datagram.data();
QHostAddress senderAddress = datagram.senderAddress();
quint16 senderPort = datagram.senderPort();
QString message = QString::fromUtf8(data);
}
// 信号连接
connect(udpSocket, &QUdpSocket::readyRead,
this, &MyClass::onReadyRead);
1.3 完整UDP服务器示例
cpp
void UdpServer::initSocket()
{
udpSocket = new QUdpSocket(this);
udpSocket->bind(QHostAddress::LocalHost, 7755);
connect(udpSocket, &QUdpSocket::readyRead,
this, &UdpServer::readPendingDatagrams);
}
void UdpServer::readPendingDatagrams()
{
while (udpSocket->hasPendingDatagrams()) {
QNetworkDatagram datagram = udpSocket->receiveDatagram();
processTheDatagram(datagram);
}
}
2. HTTP请求编程
2.1 HTTP协议简介
超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,运行在TCP之上,客户端通过HTTP请求从服务器获取数据。
2.2 QNetworkAccessManager类
QNetworkAccessManager Class 网络请求管理器
| Header: | #include |
|---|---|
| qmake: | QT += network |
| Since: | Qt 4.4 |
| Inherits: | QObject |
网络请求管理器,用于发送HTTP请求:
cpp
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
// 在.pro文件中添加: QT += network
// 创建网络请求管理器
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
// 发送GET请求
QNetworkRequest request(QUrl("http://qt-project.org"));
QNetworkReply *reply = manager->get(request);
// 发送POST请求
QNetworkRequest request(QUrl("http://example.com/api"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QByteArray postData = "{\"key\":\"value\"}";
QNetworkReply *reply = manager->post(request, postData);
// 信号连接
connect(manager, &QNetworkAccessManager::finished,
this, &MyClass::replyFinished);
// 处理响应
void MyClass::replyFinished(QNetworkReply *reply)
{
if (reply->error() == QNetworkReply::NoError) {
QByteArray data = reply->readAll();
QString response = QString::fromUtf8(data);
// 处理响应数据
} else {
// 处理错误
qDebug() << "Error:" << reply->errorString();
}
reply->deleteLater();
}

2.3 QNetworkReply类
QNetworkReply Class 网络请求数据管理器
| Header: | #include |
|---|---|
| qmake: | QT += network |
| Since: | Qt 4.4 |
| Inherits: | QIODevice |
网络请求数据管理器,用于处理网络响应:
cpp
// 读取响应数据
QByteArray data = reply->readAll();
// 信号连接
connect(reply, &QNetworkReply::finished,
this, &MyClass::onFinished);
connect(reply, &QNetworkReply::readyRead,
this, &MyClass::onReadyRead);
connect(reply, &QNetworkReply::downloadProgress,
this, &MyClass::onDownloadProgress);
// 处理下载进度
void MyClass::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
if (bytesTotal > 0) {
int percent = (bytesReceived * 100) / bytesTotal;
qDebug() << "Download progress:" << percent << "%";
}
}
2.4 完整HTTP请求示例
cpp
// 发起HTTP请求
void HttpClient::makeRequest()
{
QNetworkRequest request;
request.setUrl(QUrl("http://qt-project.org"));
request.setRawHeader("User-Agent", "MyOwnBrowser 1.0");
QNetworkReply *reply = manager->get(request);
connect(reply, &QIODevice::readyRead,
this, &HttpClient::slotReadyRead);
}
// 处理响应数据
void HttpClient::slotReadyRead()
{
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
QByteArray data = reply->readAll();
// 处理数据
}
3. 综合示例:网络工具集
以下是一个综合示例,包含UDP聊天、HTTP请求和文件下载功能:
3.1 主窗口头文件(networktool.h)
cpp
#ifndef NETWORKTOOL_H
#define NETWORKTOOL_H
#include <QMainWindow>
#include <QUdpSocket>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QTabWidget>
#include <QTextEdit>
#include <QLineEdit>
#include <QPushButton>
#include <QProgressBar>
#include <QLabel>
class NetworkTool : public QMainWindow
{
Q_OBJECT
public:
NetworkTool(QWidget *parent = nullptr);
~NetworkTool();
private slots:
// UDP相关槽函数
void initUdpSocket();
void sendUdpMessage();
void onUdpReadyRead();
// HTTP相关槽函数
void sendHttpRequest();
void onHttpFinished(QNetworkReply *reply);
void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
void startDownload();
void onDownloadFinished();
private:
void setupUI();
// UDP组件
QUdpSocket *udpSocket;
QTextEdit *udpLogTextEdit;
QLineEdit *udpMessageEdit;
QLineEdit *udpTargetIPEdit;
QLineEdit *udpTargetPortEdit;
QLineEdit *udpListenPortEdit;
QPushButton *udpSendButton;
QPushButton *udpListenButton;
// HTTP组件
QNetworkAccessManager *networkManager;
QTextEdit *httpResponseTextEdit;
QLineEdit *httpUrlEdit;
QPushButton *httpRequestButton;
// 文件下载组件
QLineEdit *downloadUrlEdit;
QLineEdit *downloadPathEdit;
QPushButton *downloadButton;
QProgressBar *downloadProgressBar;
QLabel *downloadStatusLabel;
};
#endif // NETWORKTOOL_H
3.2 主窗口实现(networktool.cpp)
cpp
#include "networktool.h"
#include <QApplication>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QFormLayout>
#include <QWidget>
#include <QDateTime>
#include <QFileDialog>
#include <QFile>
#include <QMessageBox>
NetworkTool::NetworkTool(QWidget *parent)
: QMainWindow(parent)
, udpSocket(new QUdpSocket(this))
, networkManager(new QNetworkAccessManager(this))
{
setupUI();
// UDP信号连接
connect(udpSocket, &QUdpSocket::readyRead,
this, &NetworkTool::onUdpReadyRead);
// HTTP信号连接
connect(networkManager, &QNetworkAccessManager::finished,
this, &NetworkTool::onHttpFinished);
// 按钮信号连接
connect(udpSendButton, &QPushButton::clicked,
this, &NetworkTool::sendUdpMessage);
connect(udpListenButton, &QPushButton::clicked,
this, &NetworkTool::initUdpSocket);
connect(httpRequestButton, &QPushButton::clicked,
this, &NetworkTool::sendHttpRequest);
connect(downloadButton, &QPushButton::clicked,
this, &NetworkTool::startDownload);
}
NetworkTool::~NetworkTool()
{
}
void NetworkTool::setupUI()
{
QTabWidget *tabWidget = new QTabWidget;
// UDP聊天标签页
QWidget *udpWidget = new QWidget;
udpLogTextEdit = new QTextEdit;
udpLogTextEdit->setReadOnly(true);
udpMessageEdit = new QLineEdit;
udpTargetIPEdit = new QLineEdit("127.0.0.1");
udpTargetPortEdit = new QLineEdit("8080");
udpListenPortEdit = new QLineEdit("8080");
udpSendButton = new QPushButton("发送");
udpListenButton = new QPushButton("监听");
auto *udpControlLayout = new QHBoxLayout;
udpControlLayout->addWidget(new QLabel("监听端口:"));
udpControlLayout->addWidget(udpListenPortEdit);
udpControlLayout->addWidget(udpListenButton);
udpControlLayout->addStretch();
auto *udpSendLayout = new QHBoxLayout;
udpSendLayout->addWidget(new QLabel("目标IP:"));
udpSendLayout->addWidget(udpTargetIPEdit);
udpSendLayout->addWidget(new QLabel("端口:"));
udpSendLayout->addWidget(udpTargetPortEdit);
udpSendLayout->addWidget(udpMessageEdit);
udpSendLayout->addWidget(udpSendButton);
auto *udpLayout = new QVBoxLayout;
udpLayout->addLayout(udpControlLayout);
udpLayout->addWidget(new QLabel("消息日志:"));
udpLayout->addWidget(udpLogTextEdit);
udpLayout->addLayout(udpSendLayout);
udpWidget->setLayout(udpLayout);
// HTTP请求标签页
QWidget *httpWidget = new QWidget;
httpResponseTextEdit = new QTextEdit;
httpResponseTextEdit->setReadOnly(true);
httpUrlEdit = new QLineEdit("https://httpbin.org/get");
httpRequestButton = new QPushButton("发送请求");
auto *httpControlLayout = new QHBoxLayout;
httpControlLayout->addWidget(new QLabel("URL:"));
httpControlLayout->addWidget(httpUrlEdit);
httpControlLayout->addWidget(httpRequestButton);
auto *httpLayout = new QVBoxLayout;
httpLayout->addLayout(httpControlLayout);
httpLayout->addWidget(new QLabel("响应内容:"));
httpLayout->addWidget(httpResponseTextEdit);
httpWidget->setLayout(httpLayout);
// 文件下载标签页
QWidget *downloadWidget = new QWidget;
downloadUrlEdit = new QLineEdit("https://httpbin.org/json");
downloadPathEdit = new QLineEdit;
downloadButton = new QPushButton("下载");
downloadProgressBar = new QProgressBar;
downloadStatusLabel = new QLabel("准备就绪");
QPushButton *browseButton = new QPushButton("浏览");
connect(browseButton, &QPushButton::clicked, [=]() {
QString fileName = QFileDialog::getSaveFileName(this, "保存文件", "", "所有文件 (*.*)");
if (!fileName.isEmpty()) {
downloadPathEdit->setText(fileName);
}
});
auto *downloadControlLayout = new QHBoxLayout;
downloadControlLayout->addWidget(new QLabel("下载URL:"));
downloadControlLayout->addWidget(downloadUrlEdit);
auto *downloadPathLayout = new QHBoxLayout;
downloadPathLayout->addWidget(new QLabel("保存路径:"));
downloadPathLayout->addWidget(downloadPathEdit);
downloadPathLayout->addWidget(browseButton);
auto *downloadButtonLayout = new QHBoxLayout;
downloadButtonLayout->addWidget(downloadButton);
downloadButtonLayout->addStretch();
downloadButtonLayout->addWidget(downloadStatusLabel);
auto *downloadLayout = new QVBoxLayout;
downloadLayout->addLayout(downloadControlLayout);
downloadLayout->addLayout(downloadPathLayout);
downloadLayout->addWidget(downloadProgressBar);
downloadLayout->addLayout(downloadButtonLayout);
downloadWidget->setLayout(downloadLayout);
// 添加标签页
tabWidget->addTab(udpWidget, "UDP聊天");
tabWidget->addTab(httpWidget, "HTTP请求");
tabWidget->addTab(downloadWidget, "文件下载");
setCentralWidget(tabWidget);
setWindowTitle("网络工具集");
resize(600, 400);
}
void NetworkTool::initUdpSocket()
{
quint16 port = udpListenPortEdit->text().toUShort();
if (udpSocket->bind(QHostAddress::Any, port)) {
udpLogTextEdit->append(QString("[%1] 开始监听UDP端口 %2")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(port));
udpListenButton->setEnabled(false);
} else {
udpLogTextEdit->append(QString("[%1] 无法监听端口: %2")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(udpSocket->errorString()));
}
}
void NetworkTool::sendUdpMessage()
{
QString message = udpMessageEdit->text();
if (!message.isEmpty()) {
QString targetIP = udpTargetIPEdit->text();
quint16 targetPort = udpTargetPortEdit->text().toUShort();
QByteArray data = message.toUtf8();
udpSocket->writeDatagram(data, QHostAddress(targetIP), targetPort);
udpLogTextEdit->append(QString("[%1] 发送: %2 -> %3:%4")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(message)
.arg(targetIP)
.arg(targetPort));
udpMessageEdit->clear();
}
}
void NetworkTool::onUdpReadyRead()
{
while (udpSocket->hasPendingDatagrams()) {
QNetworkDatagram datagram = udpSocket->receiveDatagram();
QString message = QString::fromUtf8(datagram.data());
QString senderInfo = QString("%1:%2")
.arg(datagram.senderAddress().toString())
.arg(datagram.senderPort());
udpLogTextEdit->append(QString("[%1] 接收自 %3: %2")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(message)
.arg(senderInfo));
}
}
void NetworkTool::sendHttpRequest()
{
QString url = httpUrlEdit->text();
if (!url.isEmpty()) {
QNetworkRequest request(QUrl(url));
QNetworkReply *reply = networkManager->get(request);
httpResponseTextEdit->append(QString("[%1] 发送请求到: %2")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(url));
}
}
void NetworkTool::onHttpFinished(QNetworkReply *reply)
{
if (reply->error() == QNetworkReply::NoError) {
QByteArray data = reply->readAll();
QString response = QString::fromUtf8(data);
httpResponseTextEdit->append(QString("响应:\n%1\n---").arg(response));
} else {
httpResponseTextEdit->append(QString("错误: %1").arg(reply->errorString()));
}
reply->deleteLater();
}
void NetworkTool::startDownload()
{
QString url = downloadUrlEdit->text();
QString filePath = downloadPathEdit->text();
if (url.isEmpty() || filePath.isEmpty()) {
QMessageBox::warning(this, "警告", "请填写URL和保存路径");
return;
}
QNetworkRequest request(QUrl(url));
QNetworkReply *reply = networkManager->get(request);
// 连接下载相关信号
connect(reply, &QNetworkReply::downloadProgress,
this, &NetworkTool::onDownloadProgress);
connect(reply, &QNetworkReply::finished,
this, &NetworkTool::onDownloadFinished);
// 保存文件
QFile *file = new QFile(filePath);
if (file->open(QIODevice::WriteOnly)) {
reply->setProperty("file", QVariant::fromValue(file));
downloadStatusLabel->setText("正在下载...");
downloadButton->setEnabled(false);
} else {
delete file;
QMessageBox::critical(this, "错误", "无法创建文件: " + file->errorString());
reply->abort();
reply->deleteLater();
}
}
void NetworkTool::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
if (bytesTotal > 0) {
int percent = (bytesReceived * 100) / bytesTotal;
downloadProgressBar->setValue(percent);
downloadStatusLabel->setText(QString("已下载: %1 / %2 (%3%)")
.arg(bytesReceived)
.arg(bytesTotal)
.arg(percent));
}
}
void NetworkTool::onDownloadFinished()
{
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
QFile *file = reply->property("file").value<QFile*>();
if (reply->error() == QNetworkReply::NoError) {
QByteArray data = reply->readAll();
if (file) {
file->write(data);
file->close();
downloadStatusLabel->setText("下载完成");
}
} else {
downloadStatusLabel->setText("下载失败: " + reply->errorString());
if (file) {
file->remove();
}
}
if (file) {
delete file;
}
downloadButton->setEnabled(true);
reply->deleteLater();
}
3.3 主函数(main.cpp)
cpp
#include "networktool.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
NetworkTool tool;
tool.show();
return app.exec();
}
#include "main.moc"
3.4 项目文件(networktool.pro)
cmake
QT += core gui network widgets
CONFIG += c++11
TARGET = NetworkTool
TEMPLATE = app
SOURCES += \
main.cpp \
networktool.cpp
HEADERS += \
networktool.h
总结
这个综合示例演示了以下Qt网络编程知识点:
-
UDP通信:
- QUdpSocket的使用
- 数据报文的发送和接收
- 广播和单播通信
-
HTTP请求:
- QNetworkAccessManager的使用
- GET和POST请求的发送
- 响应数据的处理
-
文件下载:
- 下载进度监控
- 文件保存处理
- 错误处理机制
-
综合应用技巧:
- 多种网络协议的集成
- 用户界面与网络功能的结合
- 异步操作的处理
- 资源管理和内存释放