Qt 提供了 QNetworkAccessManager 及其相关的类,方便用户编写网络接口,但向较旧的库搭建的服务器 post 时,发现在服务器端只接收到 header,body 丢失了。推测是为了安全性,Qt 或者目前比较新的库采用这个机制 —— 将 header 与 body 拆开发送。但 Qt 似乎不兼容旧协议中 header 与 body 一并发送的机制,于是改为调用 libcurl 的网络接口。记录一下使用 Qt 的 API 和 libcurl 的 API 已经调通的 https get/post 请求,两者均能正常向 postman 搭建的模拟服务器发送请求,但只有 libcurl 的接口能向 iqims 发送请求。
QNetworkAccessManager
get
QByteArray QiqimsClient::getData(QString url)
{
QByteArray retRespon;
QNetworkAccessManager manager;
QNetworkRequest request;
request.setUrl(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QSslConfiguration config = QSslConfiguration::defaultConfiguration();
config.setProtocol(QSsl::TlsV1_2);
config.setPeerVerifyMode(QSslSocket::VerifyNone);
request.setSslConfiguration(config);
QNetworkReply* reply = manager.get(request);
QEventLoop eventLoop;
connect(reply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit);
eventLoop.exec();
if (reply->error() != QNetworkReply::NoError) {
sigShowRespon(reply->errorString());
}
else {
retRespon = reply->readAll();
}
delete reply;
return retRespon;
}
post
bool QiqimsClient::QNetWorkPost(QString url, QByteArray postData)
{
QNetworkRequest request;
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json;charset=utf-8");
// request.setRawHeader("Accept", "*/*");
// request.setRawHeader("Expect", "100-continue");
// request.setRawHeader("Connection", "Keep-Alive");
// request.setHeader(QNetworkRequest::ContentLengthHeader, postData.length());
// request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
request.setUrl(url);
// skip ssl
QSslConfiguration sslConf;
sslConf.setProtocol(QSsl::TlsV1_2);
sslConf.setPeerVerifyMode(QSslSocket::VerifyNone);
request.setSslConfiguration(sslConf);
// post
// qDebug() << "send:" << oneItemObj;
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
QNetworkReply *reply = manager->post(request, postData);
// waite reply
QEventLoop eventLoop;
QTimer timer;
timer.setInterval(5000); // set 5s timeout
timer.setSingleShot(true);
connect(&timer, &QTimer::timeout, &eventLoop, &QEventLoop::quit);
connect(manager, &QNetworkAccessManager::finished, &eventLoop, &QEventLoop::quit);
timer.start();
eventLoop.exec();
if(timer.isActive()) {
timer.stop();
} else {
disconnect(manager, &QNetworkAccessManager::finished, &eventLoop, &QEventLoop::quit);
reply->abort();
qDebug() << "https request timeout";
return false;
}
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if(reply->error() != QNetworkReply::NoError) {
emit sigShowRespon(QString("Code:%1").QString::number(statusCode));
emit sigShowRespon(QString("Err:%1").arg(reply->errorString()));
reply->deleteLater();
return false;
} else {
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
emit sigShowRespon(QString("Code:%1").QString::number(statusCode));
// server reply normal
if(statusCode == 200) {
QByteArray replyContent = reply->readAll();
emit sigShowRespon(replyContent);
return true;
} else {
reply->deleteLater();
return false;
}
}
return true;
}
libcurl
libcurl 主要通过 SSL(Secure Sockets Layer 安全套接层)和 TLS(Transport Layer Security 传输层安全),在传输层对网络连接进行加密。
在 Qt 中引入 curl 库
电脑上安装了 VS2015 或者其他版本可以自己编译。
- https://curl.se/download.html 官网下载 curl 包。
- 在命令行窗口设置 VS 的环境变量,只会在当前 CMD 生效。
"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\vcvars32.bat"
- 到 curl\winbuild 目录下编译
"cd D:\curl-7.85.0\winbuild
- 编译
nmake /f Makefile.vc mode=dll VC=12
编译出来的库在 D:\curl-7.85.0\builds\libcurl-vc12-x86-release-dll-ipv6-sspi-schannel,测试一下
–insecure 表示跳过证书验证,编译成功,可以访问 https。
- 把 libcurl-vc12-x86-release-dll-ipv6-sspi-schannel 文件夹改个简单的名字 curl,放到工程目录中,在 .pro 工程文件中添加路径。
INCLUDEPATH += ./libcurl/include/curl
LIBS += -L$$PWD/libcurl/lib/ -llibcurl
- 包含头文件
#include "libcurl/include/curl/curl.h"
- 把 libcurl/bin/libcurl.dll 复制到编译出来的 .exe 同一目录下
get
QString IqimsService::curlGet(QString url)
{
//https
QString responeData;
CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if(curl) {
// set url, change QString to char* beacuse use c++ lib
char* setUrl;
QByteArray urlBa = url.toLatin1();
setUrl = urlBa.data();
curl_easy_setopt(curl, CURLOPT_URL, setUrl);
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET");
//set timeout, (s)
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3);
// set a call back function to get response
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, getUrlResponse);
// responeData is the 4th param in getUrlResponse, save respone data to it
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&responeData);
// skip ssl
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
// Perform the request, res will get the return code
res = curl_easy_perform(curl); // waite request here
// Check for errors
if(res != CURLE_OK) {
qDebug() << "curl_easy_strerror:" << curl_easy_strerror(res);
}
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return responeData;
}
post
QString IqimsService::curlPost(QString url, QByteArray postData)
{
//https
QString responeData;
CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if(curl) {
curl_slist *http_headers = NULL;
http_headers = curl_slist_append(http_headers, "Accept: application/json");
http_headers = curl_slist_append(http_headers, "Content-Type: application/json");//text/html
http_headers = curl_slist_append(http_headers, "charsets: utf-8");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_headers);
//set method to post
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
// set url, change QString to char* beacuse use c++ lib
char* setUrl;
QByteArray urlBa = url.toLatin1();
setUrl = urlBa.data();
curl_easy_setopt(curl, CURLOPT_URL, setUrl);
//set timeout, (s)
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3);
// show debug message
// curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
// set post Data, change QByteArray to char* beacuse use c++ lib
char* setPostData;
setPostData = postData.data();
curl_easy_setopt(curl,CURLOPT_POSTFIELDS, setPostData);
// set a call back function to get response
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, getUrlResponse);
// responeData is the 4th param in getUrlResponse, save respone data to it
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&responeData);
// skip ssl
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
// Perform the request, res will get the return code
res = curl_easy_perform(curl); // waite request here
// Check for errors
if(res != CURLE_OK) {
qDebug() << curl_easy_strerror(res);
}
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return responeData;
}
|