1. 崩溃的请求
"为什么我的网络请求在Windows上好好的,到了鸿蒙手机上就全部超时或者报错?"
这是很多Qt开发者在移植时发出的哀嚎。即使是一个简单的QNetworkAccessManager::get(QNetworkRequest(url)),如果URL是HTTPS的,很有可能直接返回SslHandshakeFailedError。
2. 幕后黑手:OpenSSL与根证书
Qt的QtNetwork模块依赖OpenSSL库来处理TLS/SSL握手。
在Desktop端,Qt通常会动态加载系统的OpenSSL库(libssl.so, libcrypto.so)。
但在鸿蒙(以及Android)上,情况比较复杂:
- 库缺失:Qt for OpenHarmony的预编译包里,是否包含了OpenSSL的动态库?
- 证书缺失 :OpenSSL默认会在
/etc/ssl/certs等路径查找CA根证书。鸿蒙作为移动系统,其根证书存储位置可能不同,或者Qt无法访问到系统证书库。
握手失败流程图
App QNetworkAccessManager OpenSSL Server GET https://api.example.com Initiate TLS Handshake Client Hello Server Hello + Certificate Chain Verify Certificate Looking for CA Bundle... CA Bundle NOT FOUND! Handshake Failed (Unknown CA) Error: SSL Handshake Failed App QNetworkAccessManager OpenSSL Server
3. 实战排查
查看SSL支持情况
首先,我们需要确认Qt是否找到了OpenSSL库。
cpp
#include <QSslSocket>
#include <QDebug>
void checkSsl() {
qDebug() << "Supports SSL:" << QSslSocket::supportsSsl();
qDebug() << "Build Version:" << QSslSocket::sslLibraryBuildVersionString();
qDebug() << "Loaded Version:" << QSslSocket::sslLibraryVersionString();
}
如果输出Supports SSL: false,说明缺少libssl.so和libcrypto.so。你需要将这两个库打包进你的HAP,并在CMake中正确链接或确保它们在LD_LIBRARY_PATH可搜索路径下。
解决证书问题:自带CA Bundle
如果支持SSL但依然报错QSslError::CertificateUntrusted,最通用的解决方案是自带证书链。
由于我们无法保证能读取到鸿蒙系统的根证书库(或者权限受限),我们可以将Mozilla的CA Bundle(cacert.pem)放入resources/rawfile中。
步骤:
- 下载
cacert.pem(from curl.se). - 放入
resources/rawfile/cacert.pem. - 在应用启动时,加载这个文件并设置为Qt的默认配置。
cpp
// SslHelper.cpp
#include <QSslConfiguration>
#include <QFile>
#include <QList>
#include <QSslCertificate>
// 假设我们已经有了将Rawfile拷贝到沙箱的方法 (参考文章04)
void initSslConfig() {
QString certPath = copyRawFileToSandbox("cacert.pem");
QList<QSslCertificate> caCerts = QSslCertificate::fromPath(certPath);
qDebug() << "Loaded CA certs:" << caCerts.size();
QSslConfiguration config = QSslConfiguration::defaultConfiguration();
config.setCaCertificates(caCerts);
QSslConfiguration::setDefaultConfiguration(config);
}
这样,无论系统证书库在哪里,Qt都使用我们自带的证书链进行验证,彻底解决了"Unknown CA"的问题。
4. 网络权限配置
除了SSL,最基础的权限也容易被忽略。鸿蒙应用默认是没有网络访问权限的。
Bug现象:
QNetworkReply::NetworkError 返回 AccessDeniedError 或者 ConnectionRefusedError(在真机上)。
修复:
检查module.json5:
json
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
注意:在某些鸿蒙版本中,如果访问的是非加密流量(HTTP),还需要在module.json5中配置Cleartext流量许可,或者在系统设置中开启。但在Qt侧,我们主要关注SSL问题。
5. 进阶:使用鸿蒙原生网络栈?
Qt 6.x 的 QNetworkAccessManager 是基于BSD Socket实现的。
鸿蒙提供了原生的网络能力 Network Kit (HttpClient)。
问:要不要封装鸿蒙原生的HttpClient?
答: 除非你需要通过鸿蒙系统代理(Proxy)或者通过鸿蒙特有的网络通道,否则没必要 。
修复好OpenSSL和证书问题后,QNetworkAccessManager的性能和兼容性是非常好的,而且代码完全跨平台。
只有在一种情况下推荐原生:你需要后台下载 。鸿蒙的后台任务管理很严格,Qt的网络线程在后台容易被挂起。使用鸿蒙的DownloadTask可以将下载任务托管给系统,即使App被杀掉也能继续下载。
6. 总结
网络问题通常不是代码逻辑问题,而是环境配置问题。
- OpenSSL库:必须随包分发,版本要匹配。
- CA证书 :不要依赖系统,自带
cacert.pem最稳妥。 - 权限 :别忘了申请
ohos.permission.INTERNET。
解决了这三点,你的Qt应用就能在鸿蒙的互联网世界畅行无阻。