網(wǎng)站建設注冊密碼咋弄百度一下百度百科
QT5實現(xiàn)https的post請求
- 前言
- 一、一定要有sslErrors處理
- 1、問題經(jīng)過
- 2、代碼示例
- 二、要利用抓包工具
- 1、問題經(jīng)過
- 2、wireshark的使用
- 3、利用wireshark查看服務器地址
- 4、利用wireshark查看自己構建的請求報文
- 三、返回數(shù)據(jù)只能讀一次
- 1、問題描述
- 2、部分代碼
- 總結
前言
- QNetworkAccessManager、QNetworkRequest和QNetworkReply是QT5網(wǎng)絡編程的API,三者共同完成HTTP或者HTTPS協(xié)議的通信。
- 初學者往往會程序編譯沒有問題,但是運行代碼沒有任何結果,于是不知道問題出在哪里。此時,要借助
postman
、wireshark
等工具的幫助。
本文按照問題出現(xiàn)的順序總結QT5的網(wǎng)絡編程方法。
一、一定要有sslErrors處理
1、問題經(jīng)過
我們知道,客戶端發(fā)送HTTPS的post請求,需要手動構建請求報文的請求行、請求頭部和請求體。一開始的時候,著急利用QNetworkRequest
來實現(xiàn)構建請求報文(request),但是程序運行始終沒有輸出數(shù)據(jù),以為是構建報文的格式不正確。于是簡化,構建get報文,程序運行依然沒有輸出數(shù)據(jù)。后來,參考官網(wǎng)Example、Gitee、CSDN的代碼,加入了sslErrors
槽函數(shù),程序終于有了反應。在利用QNetworkAccessManager::connectToHostEncrypted
時提示錯誤:qt.network.ssl: QSslSocket: cannot resolve EVP_PKEY_base_id
。
這是HTTPS請求錯誤,原因在于本地OpenSSL版本與Qt支持的不匹配。通過檢查、下載、編譯和配置OpenSSL源代碼,解決了這個問題。具體方法可以參考博文: 《(Linux)解決運行Qt程序時報錯》,親測有效。
如果不加sslErrors
槽函數(shù),那永遠不知道是OpenSSL的問題,還傻傻地以為是報文格式、post方法不對。
參考資料:QNetworkReply Class | Qt Network 5.15.17
2、代碼示例
httpspost.h部分代碼如下
class HttpsPost : public QObject
{Q_OBJECTQNetworkAccessManager m_networkAccessManager;public:HttpsPost();void doPost();~HttpsPost();private://發(fā)送data數(shù)據(jù)QByteArray m_sendJsonData;QVariant data;public slots:void sslErrors(const QList<QSslError> &errors);void postReadyRead(QNetworkReply *reply);
}
httpspost.cpp部分代碼如下:
#include <QtCore>
#include <QtNetwork>
#include <QSslConfiguration>
#include <QNetworkReply>
#include <QJsonObject>
#include <QJsonDocument>
#include <QJsonArray>
#include <QDebug>
#include <QVector>#include "httpspost.h"HttpsPost::HttpsPost()
{connect(&m_networkAccessManager, &QNetworkAccessManager::finished, this, &HttpsPost::postReadyRead);#if QT_CONFIG(ssl)connect(reply, &QNetworkReply::sslErrors,this, &HttpsPost::sslErrors);
#endif
}void HttpsPost::sslErrors(const QList<QSslError> &sslErrors)
{
#ifndef QT_NO_SSLforeach(const QSslError &error, sslErrors)qDebug() << "SSL error: " << error.errorString();#elseQ_UNUSED(sslErrors);
#endif
}
QNetworkReply
類有信號對象(signals)sslErrors
。這個信號可以用來發(fā)射、顯示錯誤消息。從而利用自己編寫的槽函數(shù)HttpsPost::sslErrors
來顯示、錯誤處理。
二、要利用抓包工具
1、問題經(jīng)過
編譯、安裝了OpenSSL之后,程序運行仍然沒有數(shù)據(jù)輸出。此時,考慮到底有沒有連接Web服務器,是否發(fā)送了post請求,所以想到利用網(wǎng)絡抓包工具。我用的是wireshark抓包工具,很好用,360軟件管家就有,安全有保障。
2、wireshark的使用
安裝好wireshark工具之后,首先用postman發(fā)送get、post請求,看看正確的通信過程是怎樣的。
①處:添加過濾器,只看本機和Web服務器IP地址的包。
②處:可以看到發(fā)送了post請求,點擊可以查看請求報文內容。
③處:查看請求報文的格式和內容。根據(jù)這個可以比對自己構建的請求報文是否正確。
④處:可以看到協(xié)議版本是TLSV1.2。
3、利用wireshark查看服務器地址
服務器的地址可以通過QNetworkRequest
類的構造函數(shù)來設置。例如:
QNetworkRequest m_httpRequest(QUrl("http://www.wangsansan.com/test/HttpsPostTest.php"));
有2個問題需要注意:
- QUrl地址中,http與https的連接過程、傳輸協(xié)議、端口都是不一樣的。
- https服務器地址不能用IP地址(QUrl(“
https://115.28.242.169/test/HttpsPostTest.php”)),否則會報錯:
SSL error: "The host name did not match any of the valid hosts for this certificate"
。
http服務器則可以用計算機名,也可以用IP地址。
參考資料:QNetworkRequest Class | Qt Network 5.15.17
4、利用wireshark查看自己構建的請求報文
構建請求報文要用到QNetworkRequest
類。剛開始的時候,不知道使用setHeader
和setRawHeader
的效果。利用wireshark就可以查看了。例如,QNetworkRequest::m_httpRequest.setHeader(QNetworkRequest::LocationHeader, QByteArray("/test/HttpsPostTest.php"));
的結果如下圖所示:
部分代碼如下:
void HttpsPost::doPost()
{QJsonDocument doc;QJsonObject jsonObjData;jsonObjData.insert("A", "111"); // 設置內容字段jsonObjData.insert("B", "222"); doc.setObject(jsonObjData);QString str = QString(doc.toJson());QByteArray content = str.toUtf8();int contentLength = content.length();//QSslConfiguration是QNetworkRequest的訪問設置類,用于設置協(xié)議類型支持httpsQSslConfiguration config;config.setPeerVerifyMode(QSslSocket::VerifyNone);config.setProtocol(QSsl::TlsV1_2); //構建QNetworkRequest對象,設置urlQNetworkRequest m_httpRequest(QUrl("http://www.wangsansan.com/test/HttpsPostTest.php"));//構建post請求報文// m_httpRequest.setHeader(QNetworkRequest::LocationHeader, QByteArray("/test/HttpsPostTest.php"));m_httpRequest.setHeader(QNetworkRequest::ContentTypeHeader, QByteArray("application/json; charset=utf-8"));m_httpRequest.setRawHeader("Connection", QByteArray("keep-alive"));m_httpRequest.setHeader(QNetworkRequest::ContentLengthHeader, doc.toJson().size());//m_sendJsonData.length()reply = m_networkAccessManager.post(m_httpRequest, doc.toJson());//返回數(shù)據(jù)QNetworkReply *reply = m_networkAccessManager.get(m_httpRequest);
三、返回數(shù)據(jù)只能讀一次
1、問題描述
到這里,向服務器發(fā)送post請求已經(jīng)沒有問題了。但是,運行程序仍然無法獲取數(shù)據(jù)。問題應該出在獲取返回數(shù)據(jù)上。剛開始時,復制了官網(wǎng)上的代碼,問題依然存在。Gitee、CSDN上的代碼,寫法也各不相同。后來,看了官網(wǎng)的參考文檔才知道,返回數(shù)據(jù)只能在返回數(shù)據(jù)結束之后,讀一次。
1.The QNetworkReply class contains the data and meta data related to a request posted with QNetworkAccessManager.
2.QNetworkReply is a sequential-access QIODevice, which means that once data is read from the object, it is no longer kept by the device.
QNetworkAccessManager::finished()
和QNetworkReply::finished()
,都可以發(fā)送返回數(shù)據(jù)結束的信號(signal)。然后用槽函數(shù)來獲取數(shù)據(jù)。您還可以使用QNetworkReply::isFinished()
來檢查QNetworkReply是否已完成。
參考文檔:
QNetworkAccessManager Class | Qt Network 5.15.17
QNetworkReply Class | Qt Network 5.15.17
2、部分代碼
httpspost.h如上文,httpspost.cpp部分代碼如下:
HttpsPost::HttpsPost()
{connect(&m_networkAccessManager, &QNetworkAccessManager::finished, this, &HttpsPost::postReadyRead);#if QT_CONFIG(ssl)connect(reply, &QNetworkReply::sslErrors,this, &HttpsPost::sslErrors);
#endif
}void HttpsPost::postReadyRead(QNetworkReply *reply)
{qDebug() <<"reply data:"<< QString::fromUtf8(reply->readAll());
}
運行結果舉例:
reply data: "{\"error_code\":111,\"error_msg\":\"Access token expired\"}"
總結
至此,QT5利用QNetworkAccessManager、QNetworkRequest和QNetworkReply三個類可以實現(xiàn)https的post請求。但是,還有問題需要改進:
- 返回數(shù)據(jù)的處理
- post請求報文中的正文部分,其格式、內容、字節(jié)長度等還需要驗證。Jason數(shù)據(jù)的嵌套還需要實現(xiàn)。