• QNetworkAccessManager 和 libcurl 网络接口


    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    libcurl

    libcurl 主要通过 SSL(Secure Sockets Layer 安全套接层)和 TLS(Transport Layer Security 传输层安全),在传输层对网络连接进行加密。

    在 Qt 中引入 curl 库

    电脑上安装了 VS2015 或者其他版本可以自己编译。

    1. https://curl.se/download.html 官网下载 curl 包。
    2. 在命令行窗口设置 VS 的环境变量,只会在当前 CMD 生效。
     "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\vcvars32.bat"
    
    • 1
    1. 到 curl\winbuild 目录下编译
    "cd D:\curl-7.85.0\winbuild
    
    • 1
    1. 编译
    nmake /f Makefile.vc mode=dll VC=12
    
    • 1

    在这里插入图片描述
    编译出来的库在 D:\curl-7.85.0\builds\libcurl-vc12-x86-release-dll-ipv6-sspi-schannel,测试一下
    在这里插入图片描述

    –insecure 表示跳过证书验证,编译成功,可以访问 https。

    1. 把 libcurl-vc12-x86-release-dll-ipv6-sspi-schannel 文件夹改个简单的名字 curl,放到工程目录中,在 .pro 工程文件中添加路径。
    INCLUDEPATH += ./libcurl/include/curl
    LIBS += -L$$PWD/libcurl/lib/ -llibcurl
    
    • 1
    • 2
    1. 包含头文件
    #include "libcurl/include/curl/curl.h"
    
    • 1
    1. 把 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
  • 相关阅读:
    LeetCode中等题之查找和替换模式
    代数、图算法:图基础
    Vue 快速入门之第四章 路由
    Vue项目实战之电商后台管理系统(八) 订单管理及数据统计模块
    【云原生-k8s篇】管理容器的顶级 Kubernetes工具有哪些?
    TortoiseSVN小乌龟的使用
    Redis实战案例及问题分析之秒杀功能优化(异步下单、Redis消息队列)
    nvm安装及使用,nodejs版本切换使用
    Windows环境下的ELK——logstash日志(2)
    CCAA 认证通用基础卷之(一)合格评定基础 第一章合格评定基础知识
  • 原文地址:https://blog.csdn.net/weixin_42255916/article/details/126752622