Qt 网络编程进阶:HTTP 客户端实现

发布于:2025-07-28 ⋅ 阅读:(14) ⋅ 点赞:(0)

在 Qt 应用程序中,实现高性能、可靠的 HTTP 客户端是常见需求。Qt 提供了丰富的网络模块,包括 QNetworkAccessManagerQNetworkRequestQNetworkReply 等类,用于简化 HTTP 通信。本文将深入探讨 Qt 网络编程中 HTTP 客户端的进阶实现,包括异步请求、并发控制、请求重试、数据缓存等高级技术。

一、基础 HTTP 请求实现

1. 同步 HTTP 请求
#include <QCoreApplication>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QEventLoop>
#include <QJsonDocument>
#include <QJsonObject>
#include <QDebug>

QByteArray syncHttpGet(const QUrl &url) {
    QNetworkAccessManager manager;
    QNetworkRequest request(url);
    
    // 设置请求头
    request.setHeader(QNetworkRequest::UserAgentHeader, "Qt HTTP Client");
    
    // 发送请求
    QNetworkReply *reply = manager.get(request);
    
    // 使用事件循环等待请求完成
    QEventLoop loop;
    QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
    loop.exec();
    
    // 处理响应
    QByteArray data;
    if (reply->error() == QNetworkReply::NoError) {
        data = reply->readAll();
    } else {
        qDebug() << "Request failed:" << reply->errorString();
    }
    
    // 清理资源
    reply->deleteLater();
    
    return data;
}

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);
    
    QUrl url("https://api.example.com/data");
    QByteArray response = syncHttpGet(url);
    
    if (!response.isEmpty()) {
        // 解析 JSON 响应
        QJsonDocument doc = QJsonDocument::fromJson(response);
        if (doc.isObject()) {
            QJsonObject obj = doc.object();
            qDebug() << "Response:" << obj;
        }
    }
    
    return a.exec();
}
2. 异步 HTTP 请求
#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QJsonDocument>
#include <QJsonObject>
#include <QDebug>

class HttpClient : public QObject {
    Q_OBJECT
public:
    explicit HttpClient(QObject *parent = nullptr) : QObject(parent) {
        manager = new QNetworkAccessManager(this);
    }
    
    void get(const QUrl &url) {
        QNetworkRequest request(url);
        request.setHeader(QNetworkRequest::UserAgentHeader, "Qt HTTP Client");
        
        QNetworkReply *reply = manager->get(request);
        connect(reply, &QNetworkReply::finished, this, [this, reply]() {
            handleResponse(reply);
            reply->deleteLater();
        });
    }
    
signals:
    void requestCompleted(const QJsonObject &data);
    void requestFailed(const QString &error);
    
private slots:
    void handleResponse(QNetworkReply *reply) {
        if (reply->error() == QNetworkReply::NoError) {
            QByteArray data = reply->readAll();
            QJsonDocument doc = QJsonDocument::fromJson(data);
            
            if (doc.isObject()) {
                emit requestCompleted(doc.object());
            } else {
                emit requestFailed("Invalid JSON response");
            }
        } else {
            emit requestFailed(reply->errorString());
        }
    }
    
private:
    QNetworkAccessManager *manager;
};

二、高级 HTTP 客户端功能

1. 请求重试机制
class RetryHttpClient : public QObject {
    Q_OBJECT
public:
    explicit RetryHttpClient(int maxRetries = 3, QObject *parent = nullptr)
        : QObject(parent), maxRetries(maxRetries) {
        manager = new QNetworkAccessManager(this);
    }
    
    void get(const QUrl &url) {
        currentUrl = url;
        currentRetry = 0;
        sendRequest();
    }
    
private slots:
    void handleResponse(QNetworkReply *reply) {
        QNetworkReply::NetworkError error = reply->error();
        QByteArray data = reply->readAll();
        reply->deleteLater();
        
        if (error == QNetworkReply::NoError) {
            emit requestCompleted(data);
        } else if (currentRetry < maxRetries) {
            // 可重试的错误(如网络超时、临时服务器错误)
            qDebug() << "Request failed, retrying" << currentRetry + 1 << "/" << maxRetries;
            currentRetry++;
            sendRequest();
        } else {
            emit requestFailed("Max retries exceeded: " + reply->errorString());
        }
    }
    
private:
    void sendRequest() {
        QNetworkRequest request(currentUrl);
        request.setHeader(QNetworkRequest::UserAgentHeader, "Qt HTTP Client (Retry)");
        
        // 设置超时(需要结合定时器实现)
        QNetworkReply *reply = manager->get(request);
        connect(reply, &QNetworkReply::finished, this, [this, reply]() {
            handleResponse(reply);
        });
    }
    
signals:
    void requestCompleted(const QByteArray &data);
    void requestFailed(const QString &error);
    
private:
    QNetworkAccessManager *manager;
    QUrl currentUrl;
    int currentRetry;
    int maxRetries;
};
2. 请求并发控制
class ConcurrentHttpClient : public QObject {
    Q_OBJECT
public:
    explicit ConcurrentHttpClient(int maxConcurrent = 5, QObject *parent = nullptr)
        : QObject(parent), maxConcurrent(maxConcurrent), activeRequests(0) {
        manager = new QNetworkAccessManager(this);
    }
    
    void enqueueRequest(const QUrl &url) {
        requestQueue.enqueue(url);
        processQueue();
    }
    
private slots:
    void handleResponse(QNetworkReply *reply) {
        activeRequests--;
        
        if (reply->error() == QNetworkReply::NoError) {
            QByteArray data = reply->readAll();
            emit requestCompleted(reply->request().url(), data);
        } else {
            emit requestFailed(reply->request().url(), reply->errorString());
        }
        
        reply->deleteLater();
        processQueue();
    }
    
private:
    void processQueue() {
        while (activeRequests < maxConcurrent && !requestQueue.isEmpty()) {
            QUrl url = requestQueue.dequeue();
            QNetworkRequest request(url);
            
            QNetworkReply *reply = manager->get(request);
            activeRequests++;
            
            connect(reply, &QNetworkReply::finished, this, [this, reply]() {
                handleResponse(reply);
            });
        }
    }
    
signals:
    void requestCompleted(const QUrl &url, const QByteArray &data);
    void requestFailed(const QUrl &url, const QString &error);
    
private:
    QNetworkAccessManager *manager;
    QQueue<QUrl> requestQueue;
    int maxConcurrent;
    int activeRequests;
};
3. 请求缓存机制
class CachedHttpClient : public QObject {
    Q_OBJECT
public:
    explicit CachedHttpClient(QObject *parent = nullptr) : QObject(parent) {
        manager = new QNetworkAccessManager(this);
        cacheTimeout = 3600;  // 默认缓存1小时
    }
    
    void get(const QUrl &url, bool forceRefresh = false) {
        QString cacheKey = url.toString();
        
        // 检查缓存
        if (!forceRefresh && cache.contains(cacheKey)) {
            CacheEntry entry = cache.value(cacheKey);
            if (entry.timestamp.secsTo(QDateTime::currentDateTime()) < cacheTimeout) {
                emit requestCompleted(url, entry.data);
                return;
            }
        }
        
        // 发送网络请求
        QNetworkRequest request(url);
        QNetworkReply *reply = manager->get(request);
        
        connect(reply, &QNetworkReply::finished, this, [this, reply, url, cacheKey]() {
            if (reply->error() == QNetworkReply::NoError) {
                QByteArray data = reply->readAll();
                
                // 保存到缓存
                CacheEntry entry;
                entry.data = data;
                entry.timestamp = QDateTime::currentDateTime();
                cache.insert(cacheKey, entry);
                
                emit requestCompleted(url, data);
            } else {
                emit requestFailed(url, reply->errorString());
            }
            
            reply->deleteLater();
        });
    }
    
    void setCacheTimeout(int seconds) {
        cacheTimeout = seconds;
    }
    
private:
    struct CacheEntry {
        QByteArray data;
        QDateTime timestamp;
    };
    
    QNetworkAccessManager *manager;
    QHash<QString, CacheEntry> cache;
    int cacheTimeout;
    
signals:
    void requestCompleted(const QUrl &url, const QByteArray &data);
    void requestFailed(const QUrl &url, const QString &error);
};

三、处理不同类型的 HTTP 请求

1. POST 请求
void post(const QUrl &url, const QJsonObject &jsonData) {
    QNetworkRequest request(url);
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
    
    QJsonDocument doc(jsonData);
    QByteArray data = doc.toJson();
    
    QNetworkReply *reply = manager->post(request, data);
    
    connect(reply, &QNetworkReply::finished, this, [this, reply]() {
        // 处理响应...
        reply->deleteLater();
    });
}
2. 上传文件
void uploadFile(const QUrl &url, const QString &filePath) {
    QFile file(filePath);
    if (!file.open(QIODevice::ReadOnly)) {
        emit uploadFailed("Cannot open file: " + filePath);
        return;
    }
    
    QNetworkRequest request(url);
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/octet-stream");
    
    QNetworkReply *reply = manager->post(request, &file);
    
    connect(reply, &QNetworkReply::uploadProgress, this, [this](qint64 bytesSent, qint64 bytesTotal) {
        emit uploadProgress(bytesSent, bytesTotal);
    });
    
    connect(reply, &QNetworkReply::finished, this, [this, reply, &file]() {
        file.close();
        // 处理响应...
        reply->deleteLater();
    });
}
3. 下载文件
void downloadFile(const QUrl &url, const QString &savePath) {
    QNetworkRequest request(url);
    QNetworkReply *reply = manager->get(request);
    
    QFile file(savePath);
    if (!file.open(QIODevice::WriteOnly)) {
        emit downloadFailed("Cannot open file for writing: " + savePath);
        reply->abort();
        reply->deleteLater();
        return;
    }
    
    connect(reply, &QNetworkReply::downloadProgress, this, [this](qint64 bytesReceived, qint64 bytesTotal) {
        emit downloadProgress(bytesReceived, bytesTotal);
    });
    
    connect(reply, &QNetworkReply::readyRead, this, [reply, &file]() {
        file.write(reply->readAll());
    });
    
    connect(reply, &QNetworkReply::finished, this, [this, reply, &file, savePath]() {
        file.close();
        
        if (reply->error() == QNetworkReply::NoError) {
            emit downloadCompleted(savePath);
        } else {
            // 删除不完整的文件
            QFile::remove(savePath);
            emit downloadFailed(reply->errorString());
        }
        
        reply->deleteLater();
    });
}

四、HTTP2 支持与性能优化

1. 启用 HTTP2
void enableHttp2() {
    // Qt 5.15+ 支持 HTTP2
    // 设置 ALPN 协议优先级
    QSslConfiguration config = QSslConfiguration::defaultConfiguration();
    config.setProtocol(QSsl::TlsV1_3);
    config.setAlpnProtocols({"h2", "http/1.1"});
    
    manager->setSslConfiguration(config);
}
2. 连接池优化
void optimizeConnectionPool() {
    // 设置连接超时
    manager->setTransferTimeout(30000);  // 30秒
    
    // 设置最大连接数
    QNetworkAccessManager::setMaximumConnectionCountPerHost(10);
}

五、安全与认证

1. 基本认证
void setBasicAuth(const QString &username, const QString &password) {
    QString credentials = username + ":" + password;
    QByteArray encoded = credentials.toUtf8().toBase64();
    authHeader = "Basic " + encoded;
}

// 在请求中添加认证头
QNetworkRequest request(url);
request.setRawHeader("Authorization", authHeader);
2. OAuth2 认证
void setOAuthToken(const QString &token) {
    authHeader = "Bearer " + token.toUtf8();
}

// 在请求中添加认证头
QNetworkRequest request(url);
request.setRawHeader("Authorization", authHeader);
3. SSL/TLS 配置
void configureSsl() {
    QSslConfiguration config = QSslConfiguration::defaultConfiguration();
    
    // 验证服务器证书
    config.setPeerVerifyMode(QSslSocket::VerifyPeer);
    
    // 加载 CA 证书
    QSslCertificate caCert(QSslCertificate::fromPath("/path/to/cacert.pem"));
    if (!caCert.isEmpty()) {
        config.addCaCertificate(caCert);
    }
    
    manager->setSslConfiguration(config);
}

六、完整 HTTP 客户端示例

下面是一个整合了上述功能的完整 HTTP 客户端类:

#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QTimer>
#include <QUrl>
#include <QByteArray>
#include <QJsonObject>
#include <QJsonDocument>
#include <QHash>
#include <QQueue>
#include <QDateTime>
#include <QSslConfiguration>

class AdvancedHttpClient : public QObject {
    Q_OBJECT
public:
    explicit AdvancedHttpClient(QObject *parent = nullptr);
    ~AdvancedHttpClient() override;
    
    // 请求方法
    void get(const QUrl &url, bool forceRefresh = false);
    void post(const QUrl &url, const QJsonObject &data);
    void put(const QUrl &url, const QJsonObject &data);
    void del(const QUrl &url);
    
    // 上传下载
    void uploadFile(const QUrl &url, const QString &filePath);
    void downloadFile(const QUrl &url, const QString &savePath);
    
    // 配置
    void setMaxConcurrentRequests(int count);
    void setRetryCount(int count);
    void setCacheTimeout(int seconds);
    void setBasicAuth(const QString &username, const QString &password);
    void setOAuthToken(const QString &token);
    void enableHttp2();
    void configureSsl(const QString &caCertPath = QString());
    
signals:
    void requestCompleted(const QUrl &url, const QByteArray &data);
    void requestFailed(const QUrl &url, const QString &error);
    void uploadProgress(const QUrl &url, qint64 bytesSent, qint64 bytesTotal);
    void downloadProgress(const QUrl &url, qint64 bytesReceived, qint64 bytesTotal);
    void uploadCompleted(const QUrl &url, const QByteArray &data);
    void downloadCompleted(const QUrl &url, const QString &savePath);
    void uploadFailed(const QUrl &url, const QString &error);
    void downloadFailed(const QUrl &url, const QString &error);
    
private slots:
    void onRequestFinished();
    void onUploadProgress(qint64 bytesSent, qint64 bytesTotal);
    void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
    void processQueue();
    
private:
    struct Request {
        QUrl url;
        QByteArray data;
        QNetworkAccessManager::Operation operation;
        int retries = 0;
        bool isDownload = false;
        QString filePath;
    };
    
    struct CacheEntry {
        QByteArray data;
        QDateTime timestamp;
    };
    
    QNetworkAccessManager *manager;
    QQueue<Request> requestQueue;
    QHash<QUrl, QNetworkReply*> activeRequests;
    QHash<QString, CacheEntry> cache;
    
    int maxConcurrent = 5;
    int maxRetries = 3;
    int cacheTimeout = 3600;
    QByteArray authHeader;
};

七、总结

Qt 的网络模块提供了强大而灵活的 HTTP 客户端功能,能够满足从简单请求到复杂网络应用的各种需求。通过合理使用异步请求、并发控制、请求重试和数据缓存等技术,可以构建高性能、可靠的 HTTP 客户端。在实际开发中,还应根据具体需求考虑安全认证、HTTPS 支持和性能优化等方面,确保应用程序在各种网络环境下都能稳定运行。


网站公告

今日签到

点亮在社区的每一天
去签到