C++ Qt開發:QNetworkAccessManager網絡接口組件

Qt 是一個跨平臺C++圖形界面開發庫,利用Qt可以快速開發跨平臺窗體應用程序,在Qt中我們可以通過拖拽的方式將不同組件放到指定的位置,實現圖形化開發極大的方便了開發效率,本章將重點介紹如何運用QNetworkAccessManager組件實現Web網頁訪問。

QNetworkAccessManager是Qt網絡模塊中的關鍵類,用於管理網絡訪問和請求。作爲一個網絡請求的調度中心,它爲Qt應用程序提供了發送和接收各種類型的網絡請求的能力,包括常見的GET、POST、PUT、DELETE等。這個模塊的核心功能在於通過處理QNetworkReplyQNetworkRequest來實現與網絡資源的交互。

通過QNetworkAccessManager,Qt應用程序能夠輕鬆地與遠程服務器通信,獲取數據或將數據上傳到服務器。這種網絡請求的管理不僅是異步的,以確保不會阻塞主線程,還提供了豐富的信號和槽機制,使得開發者可以靈活地處理不同階段的網絡操作。

通常,QNetworkAccessManager會與QNetworkReplyQNetworkRequest一起使用。QNetworkRequest用於封裝和配置網絡請求的各種屬性,例如URL、請求頭等。而QNetworkReply則代表了對網絡請求的響應,包含了請求返回的數據和相關信息。這三者共同協作,爲Qt應用程序提供了便捷、靈活且強大的網絡通信能力。

1.1 通用API函數

1.1.1 QNetworkAccessManager

要想實現網絡通信首先需要新建一個網絡訪問管理器,以下是QNetworkAccessManager類中的一些常用函數及其描述:

函數 描述
QNetworkAccessManager(QObject *parent = nullptr) 構造函數,創建一個QNetworkAccessManager實例。
virtual ~QNetworkAccessManager() 虛析構函數,釋放QNetworkAccessManager實例。
QNetworkReply *get(const QNetworkRequest &request) 發送GET請求,並返回與請求關聯的QNetworkReply對象。
QNetworkReply *post(const QNetworkRequest &request, QIODevice *data) 發送POST請求,並返回與請求關聯的QNetworkReply對象。
QNetworkReply *post(const QNetworkRequest &request, const QByteArray &data) 發送POST請求,並返回與請求關聯的QNetworkReply對象。
QNetworkReply *put(const QNetworkRequest &request, QIODevice *data) 發送PUT請求,並返回與請求關聯的QNetworkReply對象。
QNetworkReply *put(const QNetworkRequest &request, const QByteArray &data) 發送PUT請求,並返回與請求關聯的QNetworkReply對象。
QNetworkReply *deleteResource(const QNetworkRequest &request) 發送DELETE請求,並返回與請求關聯的QNetworkReply對象。
QNetworkReply *head(const QNetworkRequest &request) 發送HEAD請求,並返回與請求關聯的QNetworkReply對象。
QNetworkReply *sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QIODevice *data = nullptr) 發送自定義請求,並返回與請求關聯的QNetworkReply對象。
QNetworkReply *sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, const QByteArray &data) 發送自定義請求,並返回與請求關聯的QNetworkReply對象。
void setConfiguration(const QNetworkConfiguration &config) 設置網絡配置,用於定製網絡行爲。
QNetworkConfiguration configuration() const 獲取當前網絡配置。
void clearAccessCache() 清除網絡訪問緩存。
void setCache(QAbstractNetworkCache *cache) 設置網絡緩存。
QAbstractNetworkCache *cache() const 獲取當前網絡緩存。
void setCookieJar(QNetworkCookieJar *cookieJar) 設置用於管理HTTP cookie的QNetworkCookieJar
QNetworkCookieJar *cookieJar() const 獲取當前的HTTP cookie管理器。

這些函數提供了QNetworkAccessManager的核心功能,使得開發者能夠方便地進行各種類型的網絡請求,配置網絡參數,並進行相關的網絡管理操作。

1.1.2 QNetworkReply

以下是QNetworkReply類中的一些常用函數及其描述:

函數 描述
QByteArray readAll() const 讀取所有可用的數據,並返回一個QByteArray,包含從網絡回覆讀取的所有內容。
QByteArray peek(int maxSize) const 查看最多maxSize字節的可用數據,但不從緩衝區中移除。
QByteArray read(int maxSize) 從網絡回覆中讀取最多maxSize字節的數據,並將其從緩衝區中移除。
QByteArray readLine(int maxSize = 0) 從網絡回覆中讀取一行數據,最多包含maxSize字節,並將其從緩衝區中移除。
void ignoreSslErrors(const QList<QSslError> &errors = QList<QSslError>()) 忽略SSL錯誤,繼續處理網絡回覆。
void abort() 終止網絡回覆的處理,關閉底層連接。
void close() 關閉網絡回覆的處理。
QUrl url() const 返回與網絡回覆相關聯的URL。
QNetworkRequest request() const 返回生成此網絡回覆的網絡請求。
QNetworkAccessManager *manager() const 返回與網絡回覆相關聯的QNetworkAccessManager
bool isFinished() const 檢查網絡回覆是否已完成。
QNetworkReply::NetworkError error() const 返回網絡回覆的錯誤代碼。
bool hasRawHeader(const QByteArray &headerName) const 檢查網絡回覆是否包含指定原始頭。
QList<QByteArray> rawHeaderList() const 返回網絡回覆的所有原始頭的列表。
QByteArray rawHeader(const QByteArray &headerName) const 返回指定原始頭的值。
QVariant header(QNetworkRequest::KnownHeaders header) const 返回指定標準頭的值。
QList<QByteArray> rawHeaderValues(const QByteArray &headerName) const 返回指定原始頭的所有值。
QVariant attribute(QNetworkRequest::Attribute code) const 返回指定網絡請求屬性的值。
QIODevice *readAllStandardOutput() 讀取標準輸出的所有數據,並返回一個QIODevice,用於訪問讀取的內容。
QIODevice *readAllStandardError() 讀取標準錯誤的所有數據,並返回一個QIODevice,用於訪問讀取的內容。
bool isReadable() const 檢查網絡回覆是否可讀取。

這些函數提供了對QNetworkReply實例進行各種操作和查詢的方法,包括讀取回複數據、處理SSL錯誤、獲取請求信息、檢查錯誤狀態等。開發者可以根據具體需求使用這些函數來有效地與網絡回覆進行交互。

1.1.3 QNetworkRequest

以下是QNetworkRequest類中的一些常用函數及其描述:

函數 描述
QNetworkRequest(const QUrl &url) 使用給定的URL構造一個QNetworkRequest實例。
void setUrl(const QUrl &url) 設置QNetworkRequest的URL。
QUrl url() const 返回與QNetworkRequest相關聯的URL。
void setRawHeader(const QByteArray &headerName, const QByteArray &headerValue) 設置指定原始頭的值。
QByteArray rawHeader(const QByteArray &headerName) const 返回指定原始頭的值。
bool hasRawHeader(const QByteArray &headerName) const 檢查QNetworkRequest是否包含指定原始頭。
void setRawHeaderList(const QList<QByteArray> &headerList) 設置所有原始頭的列表。
QList<QByteArray> rawHeaderList() const 返回QNetworkRequest的所有原始頭的列表。
void setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value) 設置指定標準頭的值。
QVariant header(QNetworkRequest::KnownHeaders header) const 返回指定標準頭的值。
void setAttribute(QNetworkRequest::Attribute code, const QVariant &value) 設置指定網絡請求屬性的值。
QVariant attribute(QNetworkRequest::Attribute code) const 返回指定網絡請求屬性的值。
void setSslConfiguration(const QSslConfiguration &config) 設置SSL配置。
QSslConfiguration sslConfiguration() const 返回SSL配置。
void setMaximumRedirectsAllowed(int maxRedirects) 設置允許的最大重定向次數。
int maximumRedirectsAllowed() const 返回允許的最大重定向次數。
void setOriginatingObject(QObject *object) 設置發起此網絡請求的對象。
QObject *originatingObject() const 返回發起此網絡請求的對象。
bool isEmpty() const 檢查QNetworkRequest是否爲空(未設置URL)。

這些函數提供了對QNetworkRequest實例進行各種操作和查詢的方法,包括設置和獲取頭信息、設置SSL配置、設置和獲取網絡請求屬性等。開發者可以根據具體需求使用這些函數來有效地構建和管理網絡請求。

1.2 實現Web頁面訪問

要使用該模塊讀者應該在*.pro文件內包含network網絡模塊,並在頭文件中引入QNetworkAccessManagerQNetworkReplyQNetworkRequest三個類,在建立訪問時首先使用QNetworkAccessManager新增一個manager管理類,並通過QNetworkRequest類創建一個GET請求地址,通過使用manager.get方法實現對特定頁面的訪問。

當訪問完成時需要通過一個信號來實現對數據的處理,在QNetworkReply類中包含有如下表所示的信號以供讀者使用,例如當訪問被完成時則自動觸發&QNetworkReply::finished完成信號,此時只需要對該信號進行相應的處理即可,通常會使用一個槽函數來處理它。

信號 描述
finished() 當網絡請求完成時發出。
downloadProgress(qint64, qint64) 在下載過程中定期發出,提供下載進度信息。參數爲已下載的字節數和總字節數。
uploadProgress(qint64, qint64) 在上傳過程中定期發出,提供上傳進度信息。參數爲已上傳的字節數和總字節數。
readyRead() 當有可讀取的數據時發出,用於通知應用程序可以調用readAll()read()方法以獲取更多數據。
error(QNetworkReply::NetworkError) 當網絡請求發生錯誤時發出,參數爲錯誤代碼。
sslErrors(const QList<QSslError> &) 當SSL錯誤發生時發出,參數爲SSL錯誤的列表。

這些信號提供了豐富的信息,使開發者能夠在不同階段處理網絡請求。同理,在下載和上傳過程中可以使用downloadProgressuploadProgress信號來獲取進度信息,readyRead信號表示有可讀取的數據,error信號表示請求發生錯誤,sslErrors信號表示SSL相關的錯誤。

當信號被觸發時則會通過QObject::connect連接到對應的槽函數上,如下案例中所示,在槽函數內通過reply->attribute方法我們獲取到此次響應碼中的QNetworkRequest::HttpStatusCodeAttribute屬性,該屬性用來指明本次訪問的狀態值。此類屬性也有許多可供參考,如下所示;

屬性 描述
QNetworkRequest::HttpStatusCodeAttribute HTTP響應的狀態碼。
QNetworkRequest::HttpReasonPhraseAttribute HTTP響應的原因短語,如"OK"、"Not Found"等。
QNetworkRequest::RedirectionTargetAttribute 重定向目標的URL。
QNetworkRequest::ConnectionEncryptedAttribute 連接是否加密的標誌,返回一個bool值。
QNetworkRequest::SourceIsFromCacheAttribute 請求是否來自緩存的標誌,返回一個bool值。
QNetworkRequest::HttpPipeliningAllowedAttribute 是否允許HTTP流水線傳輸的標誌,返回一個bool值。
QNetworkRequest::HttpPipeliningWasUsedAttribute 是否使用了HTTP流水線傳輸的標誌,返回一個bool值。
QNetworkRequest::CustomVerbAttribute 自定義請求動作(HTTP verb)的字符串。
QNetworkRequest::User 用戶自定義的屬性,用於存儲任意類型的用戶數據。

這些屬性提供了額外的信息,使得開發者能夠更全面地瞭解和處理網絡響應。根據具體的應用需求,開發者可以選擇使用這些屬性中的一個或多個來獲取所需的信息。

#include <QCoreApplication>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    // 創建網絡訪問管理器
    QNetworkAccessManager manager;

    // 創建GET請求
    QNetworkRequest request(QUrl("http://www.baidu.com"));

    // 發送GET請求
    QNetworkReply *reply = manager.get(request);

    // 連接信號槽,處理響應
    QObject::connect(reply, &QNetworkReply::finished, [&]()
    {
        if (reply->error() == QNetworkReply::NoError)
        {
            // 獲取請求的 URL
            qDebug() << "Request URL:" << reply->request().url();

            // 輸出請求頭信息
            qDebug() << "Request Headers:";
            QList<QByteArray> requestHeaders = reply->request().rawHeaderList();
            foreach (const QByteArray &header, requestHeaders) {
                qDebug() << header << ":" << reply->request().rawHeader(header);
            }

            // 獲取響應碼
            int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
            qDebug() << "HttpStatusCodeAttribute:" << statusCode;

            // 連接是否加密的標誌
            bool connectionEncryptedAttribute = reply->attribute(QNetworkRequest::ConnectionEncryptedAttribute).toBool();
            qDebug() << "ConnectionEncryptedAttribute:" << connectionEncryptedAttribute;

            // 請求是否來自緩存的標誌
            bool sourceIsFromCacheAttribute = reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool();
            qDebug() << "SourceIsFromCacheAttribute:" << sourceIsFromCacheAttribute;

            // HTTP請求是否被允許進行流水線處理的標誌
            bool httpPipeliningAllowedAttribute = reply->attribute(QNetworkRequest::HttpPipeliningAllowedAttribute).toBool();
            qDebug() << "HttpPipeliningAllowedAttribute:" << httpPipeliningAllowedAttribute;

            // 輸出響應頭信息
            qDebug() << "Response Headers:";
            QList<QByteArray> responseHeaders = reply->rawHeaderList();
            foreach (const QByteArray &header, responseHeaders) {
                qDebug() << header << ":" << reply->rawHeader(header);
            }

            // 處理響應內容,這裏可以使用 readAll() 方法獲取響應內容
            // qDebug() << "Response Content:" << reply->readAll();
        } else
        {
            qDebug() << "Error:" << reply->errorString();
        }

        // 釋放資源
        reply->deleteLater();
        QCoreApplication::quit();
    });

    return a.exec();
}

讀者可自行編譯並運行這段代碼,觀察請求與相應數據如下圖所示;

至於如何在圖形界面中使用則就更簡單了,首先我們在mainwindow.h頭文件中定義好所需要的兩個槽函數,函數on_finished()用於在完成請求後被調用,函數on_readyRead()則用於在回調被執行後調用,並並以兩個網絡管理類的指針變量,如下所示;

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    //自定義槽函數
    void on_finished();
    void on_readyRead();

    void on_pushButton_clicked();

private:
    Ui::MainWindow *ui;
    QNetworkAccessManager networkManager;   // 網絡管理
    QNetworkReply *reply;                   // 網絡響應
};

當獲取按鈕被點擊後則開始執行讀入指定URL地址,並對該地址進行網頁訪問,同時綁定這兩個信號,一旦被觸發則自動路由到對應的槽函數上面去,如下所示;

void MainWindow::on_pushButton_clicked()
{
    // 讀入URL地址
    QString urlSpec = ui->lineEdit->text().trimmed();
    if (urlSpec.isEmpty())
    {
        QMessageBox::information(this, "錯誤", "請指定URL");
        return;
    }

    // 格式化URL
    QUrl newUrl = QUrl::fromUserInput(urlSpec);
    if (!newUrl.isValid())
    {
        QMessageBox::information(this, "錯誤", QString("無效URL: %1").arg(urlSpec));
        return;
    }

    // 訪問頁面
    reply = networkManager.get(QNetworkRequest(newUrl));

    // 完成時的槽函數綁定
    connect(reply, SIGNAL(finished()), this, SLOT(on_finished()));

    // 讀入數據的槽函數綁定
    connect(reply, SIGNAL(readyRead()), this, SLOT(on_readyRead()));

}

相對應的,在on_finished()槽函數中我們將響應頭讀出並輸出到文本框中,在on_readyRead()槽函數中則是對整個網站頁面源代碼的輸出功能,完整代碼如下所示;

void MainWindow::on_finished()
{
    // 獲取響應碼
    int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();

    if(statusCode == 200)
    {
        ui->plainTextEdit_2->appendPlainText("響應頭數據:");
        // 輸出響應頭信息
        QList<QByteArray> responseHeaders = reply->rawHeaderList();
        foreach (const QByteArray &header, responseHeaders)
        {
            ui->plainTextEdit_2->appendPlainText(header + " : " + reply->rawHeader(header));
        }
    }
}

// 讀入頁面源代碼
void MainWindow::on_readyRead()
{
    ui->plainTextEdit->setPlainText(reply->readAll());
}

運行代碼,讀者可自行輸入特定的網站進行讀取測試,如下所示(完整代碼請參考課件部分);

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章