【QT/C++】Qt网络编程进阶:UDP通信和HTTP请求的基本原理和实际应用(超详细)

【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. 接收方绑定到特定端口等待接收数据
  3. 数据报文独立传输,不保证顺序和可靠性

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网络编程知识点:

  1. UDP通信

    • QUdpSocket的使用
    • 数据报文的发送和接收
    • 广播和单播通信
  2. HTTP请求

    • QNetworkAccessManager的使用
    • GET和POST请求的发送
    • 响应数据的处理
  3. 文件下载

    • 下载进度监控
    • 文件保存处理
    • 错误处理机制
  4. 综合应用技巧

    • 多种网络协议的集成
    • 用户界面与网络功能的结合
    • 异步操作的处理
    • 资源管理和内存释放
相关推荐
断剑zou天涯39 分钟前
【算法笔记】窗口内最大值或最小值的更新结构
java·笔记·算法
Naiva2 小时前
【小技巧】Microchip 把 MPLAB X IDE工程编码改成 UTF-8
笔记
YouEmbedded2 小时前
解码UDP
linux·udp
m***D2864 小时前
云原生网络
网络·云原生
u***27614 小时前
C#数据库操作系列---SqlSugar完结篇
网络·数据库·c#
薛慕昭4 小时前
嵌入式 C 语言猜大小游戏设计与实现
c语言·游戏
散峰而望4 小时前
C++数组(二)(算法竞赛)
开发语言·c++·算法·github
利刃大大5 小时前
【动态规划:背包问题】完全平方数
c++·算法·动态规划·背包问题·完全背包
月光技术杂谈5 小时前
实战:C驱动框架嵌入Rust模块的互操作机制与完整流程
c语言·开发语言·rust·ffi·跨语言·bindgen·互操作
笑非不退5 小时前
C# c++ 实现程序开机自启动
开发语言·c++·c#