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

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

  • 本文主要详细说明Qt中的UDP通信和HTTP请求的基本原理和实际应用。

(关注不迷路哈!!!)


1. UDP通信编程

QUdpSocket Class 类

HeaderQUdpSocket 头文件
qmakeQT += network 模块
InheritsQAbstractSocket 父类

1.1 UDP通信流程

在这里插入图片描述

UDP(User Datagram Protocol)是一种无连接的传输协议,通信流程相对简单:

  1. 发送方直接发送数据报文到指定地址和端口
  2. 接收方绑定到特定端口等待接收数据
  3. 数据报文独立传输,不保证顺序和可靠性

1.2 QUdpSocket类

#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服务器示例

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请求:

#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

网络请求数据管理器,用于处理网络响应:

// 读取响应数据
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请求示例

// 发起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)

#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)

#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)

#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)

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

    • 多种网络协议的集成
    • 用户界面与网络功能的结合
    • 异步操作的处理
    • 资源管理和内存释放
posted @ 2026-01-26 21:16  clnchanpin  阅读(11)  评论(0)    收藏  举报