【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. 综合应用技巧

    • 多种网络协议的集成
    • 用户界面与网络功能的结合
    • 异步操作的处理
    • 资源管理和内存释放
相关推荐
轻舟客丶2 小时前
ORA-03113的解决方案
数据库·经验分享·笔记·oracle
老虎06272 小时前
黑马点评学习笔记07(缓存工具封装)
笔记·学习·缓存
Yurko132 小时前
【C语言】选择结构和循环结构的进阶
c语言·开发语言·学习
闲人编程3 小时前
用Python和Telegram API构建一个消息机器人
网络·python·机器人·api·毕设·telegram·codecapsule
掘根3 小时前
【Docker】网络
网络·docker·容器
范纹杉想快点毕业3 小时前
12个月嵌入式进阶计划ZYNQ 系列芯片嵌入式与硬件系统知识学习全计划(基于国内视频资源)
c语言·arm开发·单片机·嵌入式硬件·学习·fpga开发·音视频
大邳草民3 小时前
深入理解 Python 的“左闭右开”设计哲学
开发语言·笔记·python
实心儿儿3 小时前
C++ —— list
开发语言·c++
仟千意3 小时前
C++:c++基础知识
c++