前言
在现代应用程序中,安全地传输数据变得越来越重要。qt提供了一套完整的网络api来支持http和https通信。然而,在实际开发过程中,开发者可能会遇到ssl相关的错误,例如“tls initialization failed”,cant't open ssl.lib等问题。本文将介绍如何在qt中使用https进行网络访问,网上搜到的结果大都是错的。这里并提供解决tls初始化失败和ssl库问题的方法。
一、qt中使用https的基本概念
qt使用qnetworkaccessmanager
和qnetworkreply
类来处理网络请求。对于https请求,qt会自动使用ssl/tls进行加密通信。以下是使用https的基本步骤:
- 创建
qnetworkaccessmanager
实例。 - 使用
get()
、post()
等方法发起请求。 - 连接
finished()
信号以处理响应。 - 检查
qnetworkreply
的状态和错误。
二、tls初始化失败的原因
tls初始化失败通常是因为qt没有正确配置或找到ssl库。这可能是由于以下原因:
- 缺少必要的ssl库文件。
- 编译qt时未启用ssl支持。
- 系统环境变量未正确设置。
三、解决tls初始化失败的步骤
确保你的项目中包含了网络模块(core
和network
模块通常默认包含ssl支持)。
注意,你的yourprj.pro工程文件配置中只需要: qt += network即可。不需要向网上说的那样又是配置config += openssl又是增加libs += -llib -lssl -lcrypto。这样搞反倒是错的,会报can't open ssl.lib。其实关于ssl的库qt安装时已经包含了,编译时也会自动链接成功,编译成功。
在你的qt应用程序中,尝试创建一个qsslsocket
或qsslconfiguration
对象,并使用它来发起https请求。如果qt支持ssl,这些类应该能够正常使用。能否编译通过,编译通过则没问题。
唯一需要注意的是:
运行后访问https报错,提示tls initialization failed。这是因为qt自带的libssl-1_1.dll很扯,位置在qt\qt5.14.2\tools\qtcreator\bin\libssl-1_1.dll, 是个32位的库,提供还不提供全啊,缺少64位的库。咋知道它是32位的库?简单办法文本打开后看到pe..l....的内容,说明它是32位的库(64位的库打开后看二进制能看到pe..d..的内容)。解决办法也简单,网上找到64位的库,名字叫libssl-1_1-x64.dll 和libcrypto-1_1-x64.dll,下载后把它放入你的工具链的bin目录下,我的是在 qt\qt5.14.2\5.14.2\msvc2017_64\bin下。
简单使用
以下是一个简单的示例,展示如何在qt中发起https请求:
首先在工程的pro文件中,增加:
qt += network
包含相应的头文件:
#include <qnetworkaccessmanager>
#include <qnetworkrequest>
#include <qnetworkreply>
#include <qcoreapplication>
#include <qnetworkaccessmanager>
#include <qnetworkrequest>
#include <qnetworkreply>
#include <qdebug>
int main(int argc, char *argv[]) {
qcoreapplication app(argc, argv);
// 创建网络访问管理器
qnetworkaccessmanager manager;
// 创建请求
qnetworkrequest request(qurl("https://www.example.com"));
request.setattribute(qnetworkrequest::followredirectsattribute, true);
// 发起https get请求
qnetworkreply *reply = manager.get(request);
// 连接信号以处理响应
qobject::connect(reply, &qnetworkreply::finished, [&]() {
if (reply->error() == qnetworkreply::noerror) {
qdebug() << "https request succeeded!";
qdebug() << "response:" << reply->readall();
} else {
qdebug() << "https request failed with error:" << reply->errorstring();
}
reply->deletelater();
});
// 连接错误信号
qobject::connect(reply, &qnetworkreply::erroroccurred, [&](qnetworkreply::networkerror error) {
qdebug() << "error occurred:" << error;
});
// 启动事件循环
return app.exec();
}
示例中并没有设置ssl配置,因为大多数情况下qt会自动处理ssl配置。不配置也行。但是,如果你需要自定义ssl配置,可以这样:
// 获取默认ssl配置
qsslconfiguration sslconfig = qsslconfiguration::defaultconfiguration();
// 自定义ssl配置,例如信任特定的ca证书
sslconfig.setpeerverifymode(qsslsocket::verifypeer);
sslconfig.setprotocol(qssl::tlsv1_2); // 指定使用tls 1.2协议
// 应用ssl配置到请求
request.setsslconfiguration(sslconfig);
自定义ssl配置通常只在需要特殊配置的情况下使用,例如在自签名证书或特定协议版本的情况下。对于大多数https请求,qt的默认配置足够。
下载文件示例
实现一个通过https链接下载文件的功能:
mainwindows.h头文件中增加:
private:
ui::mainwindow *ui;
qtcpserver *server;
qtcpsocket *clientsocket;
qnetworkaccessmanager *networkmanager;
qnetworkreply *networkreply;
qfile *m_file;
qstring m_videourl;
}
void mainwindow::ondownloadprogress(qint64 bytesreceived, qint64 bytestotal)
{
if (bytestotal > 0) {
int progress = static_cast<int>((bytesreceived * 100) / bytestotal);
ui->te_result->append(qstring("download progress: %1%").arg(progress));
ui->progress->setvalue(progress);
}
}
void mainwindow::onfinished()
{
if (networkreply->error()) {
ui->te_result->append(qstring("download failed: %1").arg(networkreply->errorstring()));
} else {
m_file->write(networkreply->readall());
m_file->close();
ui->te_result->append("download completed");
}
networkreply->deletelater();
networkreply = nullptr;
if (m_file) {
m_file->deletelater();
m_file = nullptr;
}
}
void mainwindow::on_btndown_clicked()
{
// 开始下载视频
if(!m_videourl.isempty()){
ui->te_result->append("begin download:");
qurl url(m_videourl);
qnetworkrequest request(url);
networkreply = networkmanager->get(request);
connect(networkreply, &qnetworkreply::downloadprogress, this, &mainwindow::ondownloadprogress);
connect(networkreply, &qnetworkreply::finished, this, &mainwindow::onfinished);
// 创建文件
m_file = new qfile("downloaded_video.mp4", this);
if (!m_file->open(qiodevice::writeonly)) {
ui->te_result->append("failed to open file for writing");
delete m_file;
m_file = nullptr;
return;
}
}else{
ui->te_result->append("begin download:");
ui->te_result->append("error,no videourl!");
return;
}
}
发表评论