QT5实现https的post请求(QNetworkAccessManager、QNetworkRequest和QNetworkReply)

news2024/11/15 12:17:21

QT5实现https的post请求

  • 前言
  • 一、一定要有sslErrors处理
    • 1、问题经过
    • 2、代码示例
  • 二、要利用抓包工具
    • 1、问题经过
    • 2、wireshark的使用
    • 3、利用wireshark查看服务器地址
    • 4、利用wireshark查看自己构建的请求报文
  • 三、返回数据只能读一次
    • 1、问题描述
    • 2、部分代码
  • 总结


前言

  1. QNetworkAccessManager、QNetworkRequest和QNetworkReply是QT5网络编程的API,三者共同完成HTTP或者HTTPS协议的通信。
  2. 初学者往往会程序编译没有问题,但是运行代码没有任何结果,于是不知道问题出在哪里。此时,要借助postmanwireshark等工具的帮助。

本文按照问题出现的顺序总结QT5的网络编程方法。

一、一定要有sslErrors处理

1、问题经过

我们知道,客户端发送HTTPS的post请求,需要手动构建请求报文的请求行、请求头部和请求体。一开始的时候,着急利用QNetworkRequest来实现构建请求报文(request),但是程序运行始终没有输出数据,以为是构建报文的格式不正确。于是简化,构建get报文,程序运行依然没有输出数据。后来,参考官网Example、Gitee、CSDN的代码,加入了sslErrors槽函数,程序终于有了反应。在利用QNetworkAccessManager::connectToHostEncrypted时提示错误:qt.network.ssl: QSslSocket: cannot resolve EVP_PKEY_base_id
这是HTTPS请求错误,原因在于本地OpenSSL版本与Qt支持的不匹配。通过检查、下载、编译和配置OpenSSL源代码,解决了这个问题。具体方法可以参考博文: 《(Linux)解决运行Qt程序时报错》,亲测有效。
如果不加sslErrors槽函数,那永远不知道是OpenSSL的问题,还傻傻地以为是报文格式、post方法不对。


参考资料:QNetworkReply Class | Qt Network 5.15.17


2、代码示例

httpspost.h部分代码如下

class HttpsPost : public QObject
{
    Q_OBJECT
    QNetworkAccessManager m_networkAccessManager;

public:
    HttpsPost();
    void doPost();
    ~HttpsPost();

private:
    //发送data数据
    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_SSL
    foreach(const QSslError &error, sslErrors)
        qDebug() << "SSL error: " << error.errorString();

#else
    Q_UNUSED(sslErrors);
#endif
}

QNetworkReply类有信号对象(signals)sslErrors。这个信号可以用来发射、显示错误消息。从而利用自己编写的槽函数HttpsPost::sslErrors来显示、错误处理。

二、要利用抓包工具

1、问题经过

编译、安装了OpenSSL之后,程序运行仍然没有数据输出。此时,考虑到底有没有连接Web服务器,是否发送了post请求,所以想到利用网络抓包工具。我用的是wireshark抓包工具,很好用,360软件管家就有,安全有保障。

2、wireshark的使用

安装好wireshark工具之后,首先用postman发送get、post请求,看看正确的通信过程是怎样的。
①处:添加过滤器,只看本机和Web服务器IP地址的包。
②处:可以看到发送了post请求,点击可以查看请求报文内容。
③处:查看请求报文的格式和内容。根据这个可以比对自己构建的请求报文是否正确。
④处:可以看到协议版本是TLSV1.2。
在这里插入图片描述

3、利用wireshark查看服务器地址

服务器的地址可以通过QNetworkRequest类的构造函数来设置。例如:

QNetworkRequest m_httpRequest(QUrl("http://www.wangsansan.com/test/HttpsPostTest.php"));

有2个问题需要注意:

  1. QUrl地址中,http与https的连接过程、传输协议、端口都是不一样的。
  2. 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类。刚开始的时候,不知道使用setHeadersetRawHeader的效果。利用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的访问设置类,用于设置协议类型支持https
    QSslConfiguration config;
    config.setPeerVerifyMode(QSslSocket::VerifyNone);
    config.setProtocol(QSsl::TlsV1_2);  
    //构建QNetworkRequest对象,设置url
    QNetworkRequest 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());
    //返回数据
    QNetworkReply *reply = m_networkAccessManager.get(m_httpRequest);

三、返回数据只能读一次

1、问题描述

到这里,向服务器发送post请求已经没有问题了。但是,运行程序仍然无法获取数据。问题应该出在获取返回数据上。刚开始时,复制了官网上的代码,问题依然存在。Gitee、CSDN上的代码,写法也各不相同。后来,看了官网的参考文档才知道,返回数据只能在返回数据结束之后,读一次

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(),都可以发送返回数据结束的信号(signal)。然后用槽函数来获取数据。您还可以使用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三个类可以实现https的post请求。但是,还有问题需要改进:

  1. 返回数据的处理
  2. post请求报文中的正文部分,其格式、内容、字节长度等还需要验证。Jason数据的嵌套还需要实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2122333.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

单片机-STM32 看门狗(八)

目录 一、看门狗概念 1、定义&#xff1a; 二、单片机中的看门狗 1、功能描述&#xff1a; 2、看门狗设置部分 预分频寄存器(IWDG_PR) 3、窗口看门狗 特性&#xff1a; 4、看门狗配置&#xff1a; 一、看门狗概念 看门狗--定时器&#xff08;不属于基本定时器、通用定…

HTTP 协议和 APACHE 服务

WEB 服务基础 Internet 因特网 因特网是 Internet 的中文译名 在 20 世纪 60 年代&#xff08;冷战时期&#xff09;&#xff0c;美国国防部高等研究计划署&#xff08;ARPA&#xff09;出于军事上的目的&#xff0c;建立了 ARPA 网络&#xff0c;该网络由四个分布在不同地方…

FreeRTOS基础入门——FreeRTOS互斥信号量(十六)

个人名片&#xff1a; &#x1f393;作者简介&#xff1a;嵌入式领域优质创作者&#x1f310;个人主页&#xff1a;妄北y &#x1f4de;个人QQ&#xff1a;2061314755 &#x1f48c;个人邮箱&#xff1a;[mailto:2061314755qq.com] &#x1f4f1;个人微信&#xff1a;Vir2025WB…

本地部署Llama 3.1大模型

Meta推出的Llama 3.1系列包括80亿、700亿、4050亿参数版本&#xff0c;上下文长度扩展至12.8万tokens&#xff0c;并增加了对八种语言的支持。 部署模型需要用到Ollama的一个工具&#xff0c;访问官方网站https://ollama.com 点击下载&#xff0c;选择下载你对应的操作系统下…

opencv图像透视处理

引言 在图像处理与计算机视觉领域&#xff0c;透视变换&#xff08;Perspective Transformation&#xff09;是一种重要的图像校正技术&#xff0c;它允许我们根据图像中已知的四个点&#xff08;通常是矩形的四个角&#xff09;和目标位置的四个点&#xff0c;将图像从一个视…

2024.9.10 作业

代码&#xff1a; /*******************************************/ 文件名&#xff1a;widget.h /*******************************************/ #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QLabel> #include <QTimeEdit> #includ…

opencv学习:信用卡卡号识别

该代码用于从信用卡图像中自动识别和提取数字信息。该系统将识别信用卡类型&#xff0c;并输出信用卡上的数字序列。 1.创建命令行参数 数字模板 信用卡 # 创建命令行参数解析器 ap argparse.ArgumentParser() # 添加命令行参数 -i/--image&#xff0c;指定输入图像路径 ap.…

破局DRG/DIP亏损,医院应该怎么做

DRG/DIP付费实施后&#xff0c;医院各临床科室可结合前期数据积累&#xff0c;根据DRG/DIP专科病组/病种四级手术占比与医疗收入占比之间的变化关系、建立DRG/DIP战略分布象限图&#xff0c;将病组分为优势病组&#xff08;病种&#xff09;、潜力病组&#xff08;病种&#xf…

线程(Thread)

目录 线程&#xff08;Thread&#xff09; 线程的创建方式 实现方式 Runnable和Callable的区别 线程的命名和优先级 线程的六种状态 线程的插队 线程的中断 线程的让出 守护线程 设置线程为守护线程 sleep()和wait()的区别 线程的同步synchronized锁 语法格式 实现…

在线动漫信息平台

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;Spring Boot框架 工具&#xff1a;IDEA/Eclipse、Navicat、Maven 系统展示 首页 会员后台 管理员…

day-52 下一个排列

思路 从后向前遍历数组&#xff0c;把遍历过的元素加入一个有序链表&#xff0c;没变里一个元素判断链表中是否有元素大于当前遍历元素&#xff0c;如果有&#xff0c;把链表中大于当前遍历元素的元素集合中最小的那一个元素赋给当前元素&#xff0c;然后将链表中剩余元素依次赋…

建造者模式builder

此篇为学习笔记&#xff0c;原文链接 https://refactoringguru.cn/design-patterns/builder 能够分步骤创建复杂对象。 该模式允许你使用相同的创建代码生成不同类型和形式的对象

JavaWeb【day14】--(SpingBoot原理)

SpingBoot原理 在前面十多天的课程当中&#xff0c;我们学习的都是web开发的技术使用&#xff0c;都是面向应用层面的&#xff0c;我们学会了怎么样去用。而我们今天所要学习的是web后端开发的最后一个篇章springboot原理篇&#xff0c;主要偏向于底层原理。 我们今天的课程安…

2-2 opencv实战进阶系列 多边形识别

目录 一、不说废话&#xff0c;先上现象 二、前言 三、思路讲解 step1&#xff1a;用阈值编辑器对图像进行处理。 step2&#xff1a;应用阈值进行二值化 step3&#xff1a;轮廓查找 step4&#xff1a; 显示文字 四、完整代码贴出 五、现象展示 六、结语 一、不说废话&…

在单向链表中找环

在单向链表中找环也是有多种办法&#xff0c;不过快慢双指针方法是其中最为简洁的方法之一&#xff0c;接下来介绍这种方法。 首先两个指针都指向链表的头部&#xff0c;令一个指针一次走一步&#xff0c;另一个指针一次走两步&#xff0c;如果它们相遇了&#xff0c;证明有环…

数据结构(7.2_1)——顺序查找

顺序查找&#xff0c;又叫"线性查找"&#xff0c;通常用于线性表&#xff08;或者顺序表和链表&#xff09;。 算法思想&#xff1a;从头到尾全部查找出来&#xff08;或者反过来也OK&#xff09; 顺序查找的实现 typedef struct {//查找表的数据结构(顺序表)Elem…

再遇“类和对象”

一、类的默认成员函数 默认成员函数就是用户没有显式实现&#xff0c;编译器会自动生成的成员函数称为默认成员函数。一个类&#xff0c;我们不写的情况下编译器会默认生成以下6个默认成员函数&#xff0c;需要注意的是这6个中最重要的是前4个&#xff0c;最后两个取地址重载不…

visio修改默认字体、颜色、形状格式、连接线格式

设计中取消勾选“将主题应用于新建的形状” 在开发工具中打开绘图资源管理器&#xff0c;并分别修改纯文本、连接线、主题的样式

文本转化为声音

在许多场景下需要将文本转化为MP3格式&#xff0c;本文将实现文本转化为声音&#xff0c;并且将声音保存为MP3格式。本文一朱自清的《春》为例&#xff0c;要实现阅读《春》并且转化为mp3格式的音频文件。 1 导入包 import pyttsx3 from docx import Document def read_word_…

ubuntu内核升级后的问题修复

文章目录 需求当前环境禁止内核更新安装内核修复/usr/include/dlocate 测试 需求 升级后的常见问题 驱动程序不兼容: 新内核版本可能导致某些硬件驱动程序不再兼容&#xff0c;尤其是专有驱动程序或第三方驱动程序。启动问题:内核更新可能导致启动问题&#xff0c;例如无法启动…