本文翻译自Qt Network in Qt 6
原文作者:Qt公司高级软件工程师,Mårten Nordheim, Timur Pocheptsov
校审:York Chen
小编注:原文发布于2020年10月5日,了解Qt 6系列最新功能,请访问https://www.qt.io/zh-cn/product/qt6/technical-specifications。最新Qt产品下载,请访问https://www.qt.io/zh-cn/download
这篇文章将总结在Qt 6中Qt Network模块的最新更新和修改,以及未来可能的发展方向。
QNetworkAccessBackend
QNetworkAccessBackend是一个抽象基类,是我们缓存、文件和ftp后端的接口。QNetworkAccessBackend在Qt中已存在很长一段时间,但一直无法在Qt Network模块之外被恰当使用。现在终于可以了!在Qt 6中,我们计划将ftp后端移出Qt Network,并作为插件单独分发。为此,我们使QNetworkAccessBackend外部接口更加友好,并使QNetworkAccessManager能够在运行时动态加载这些插件。与我们其他插件接口一样,它不像Qt那样严格保证向下兼容,同时需要连接到QtNetworkPrivate才能使用。
我们希望能让各个后端的某些行为更具指向性。例如,非网络后端不需要我们为目标URL准备代理列表。为此,我们添加了三个枚举,每个枚举都有一些值:
- TargetType
- SecurityFeatures
- IOFeatures
其中,“TargetType”为必要项,当前仅指定后端支持的目标是联网的还是本地的。
基于这个方式,如果某些特性未启用,则对应函数可能无法工作。一个类似案例就是属于IO功能的“ ZeroCopy”,它会激活函数readPointer()和advanceReadPointer()。如果未激活该特性,则无法调用这两个函数。开发该功能的主要意图是,我们可以在不破坏任何现有代码的情况下进行类似的改进。新代码必须选择启用新功能。不过,重申一下:我们不能保证网络访问后端插件的向后兼容性与用Qt开发的应用一样好。
要实现网络访问的后端插件,您需要实现一个继承自QNetworkAccessBackendFactory的类,该类负责后端的创建工作,前提是支持请求的scheme。您还需要创建一个继承自QNetworkAccessBackend的类,并重构所有纯虚函数以及'read()'或'readPointer()'以及'advanceReadPointer()'(如果您的类支持'ZeroCopy'功能) 。对于现有使用,QNetworkAccessCacheBackend和QNetworkAccessFileBackend均已更新到新接口,尽管它们直接打包到Qt库而不是作为插件分发。
网络协议
在Qt 6中,我们放弃了SPDY的支持。SPDY是具有开放规范的实验性协议,主要由谷歌开发。SPDY成为HTTP/2协议的前身和原型。HTTP/2的引入使SPDY被废弃,现在SPDY已被弃用。
在维持与HTTP/1.1的兼容性(比如方法、状态代码、URI和大多数header字段)的兼容性的同时,HTTP/2还添加了:
- HTTP表头的数据压缩
- 多个请求复用单个TCP连接
- HTTP/2服务器推送
- 允许客户端和服务器选择HTTP/2或HTTP/1.1的协商机制
在Qt 6之前,必须通过设置以下一个属性来手动启用HTTP/2协议:
- QNetworkRequest::Http2AllowedAttribute
- QNetworkRequest::Http2DirectAttribute,
例如:
request.setAttribute(QNetworkRequest::Http2AllowedAttribute, true);
对于“ Http2AllowedAttribute”,QNetworkAccessManager将“ALPN”协议用于“ https” scheme,将“h2c”标头用于“ http” scheme。如果您预先知道服务器以所谓的直接模式支持HTTP/2,无需协议协商,则第二个属性很有用。
在Qt 6中,默认启用HTTP/2支持:这意味着属性'Http2AllowedAttribute'设置为'true'。如果无法协商HTTP/2,则QNetworkAccessManager将回退到HTTP/1.1。如果您的应用程序只能使用HTTP/1.1,则可针对新的网络请求将属性'Http2AllowedAttribute'设置为'false':
request.setAttribute(QNetworkRequest::Http2AllowedAttribute, false);
此“默认启用”规则有一个例外:
QNetworkAccessManager::connectToHostEncrypted(host, port, configuration);
使用此功能,应用程序可预先连接到服务器,无需发送任何请求。如果我们要在这样的连接上启用HTTP/2,则该连接对于希望使用HTTP/1.1并通过使用请求属性禁用HTTP/2的应用程序将毫无用处。通过在“ configuration”参数中设置所需的协议列表,可以显式启用HTTP/2,例如:
auto tlsConfig = QSslConfiguration::defaultConfiguration();
tlsConfig.setAllowedNextProtocols({QSslConfiguration::ALPNProtocolHTTP2});
manager.connectToHostEncrypted(host, port, tlsConfig);
默认重定向策略已更改
另一个可能影响您网络代码的更改是QNetworkAccessManager在Qt 6中使用的默认重定向策略。该策略API最初是在Qt 5中引入的,需要注意在Qt 6中切换为默认自动重定向处理。
QNetworkAccessManager支持几种重定向策略,由枚举QNetworkRequest :: RedirectPolicy描述。这些策略是:
- ManualRedirectPolicy,
- NoLessSafeRedirectPolicy
- SameOriginRedirectPolicy
- UserVerifiedRedirectPolicy
从Qt 6开始,QNetworkAccessManager发出请求时将使用的默认重定向策略是QNetworkRequest :: NoLessSafeRedirectPolicy(该策略禁止从“ https”重定向到“ http”)。
如果您的应用程序使用连接到QNetworkReply :: redirected()的槽来处理重定向,在Qt 6中必须将策略设置为QNetworkRequest :: ManualRedirectPolicy:
req.setAttribute(RedirectPolicyAttribute, ManualRedirectPolicy);
Bearer management已删除
Bearer management这个机制是过去为支持漫游和管理特定设备上的连接而设置的。它也可以在连接互联网时选择使用设备的哪个网络接口。现在,情况已不再如此,操作系统通常会自行处理漫游,并自动选择最佳的网络接口来发送数据,应用程序通常无法(或没有方便的方法)影响这些决策。
尽管QNetworkManager提供了API来选择要使用的接口或配置正在使用的接口,但是新的实际情况意味着没有哪个Qt支持的平台为Qt提供可靠的方式来执行请求。同时,跟踪网络状态的尝试引发了各种问题,例如Windows上的WiFi流量过大,导致ping时间较长,或者短暂的网络中断后,Qt无法识别网络的恢复,导致所有后续请求失败。
鉴于新的实际情况也使Bearer Management在很大程度上变得多余,我们决定Qt Network是时候淘汰这一部分了。
仍然有用的一个功能是检查连接状态并收到连接状态变化的通知。截止到Qt 6.0这个功能仍然保留,但我们已收集了一些用例并有望在未来的小版本中替换。如果您有希望被包含的特殊用例,或者想要跟踪此任务的进度,请留意QTBUG-86966。
QSslSocket
Qt 6的QSslSocket增加了一个API,提供在收发过程中,包含在TLS协议内的报警消息的通知。我们有两个新的枚举类型:
- QSsl :: AlertLevel描述问题的严重性
- QSsl :: AlertType指出问题的类型
QSslSocket有两个新的信号:
- QSslSocket::alertSent()
- QSslSocket::alertReceived()
这些信号还会报告问题的文本描述(如果TLS库提供的话)。
另一项更改与TLS握手相关,新功能能够在握手仍在进行时尽早报告握手期间遇到的错误。这些错误直接从验证回调函数中返回。QSslConfiguration有一个新的getter和setter:
- QSslSocket有一个新的信号:
- void handshakeInterruptedOnError();
和一个要求必须忽略错误的函数:
- void continueInterruptedHandshake();
如果不忽略错误,则底层TLS库将向对等方发送警报消息。
对于使用QNetworkAccessManager的应用程序,该API有点偏底层,可能没有太大的意义。但对于那些与QSslSocket直接打交道的调试过程或实现某些错误或警告处理逻辑时可能就非常方便了。目前,这个改动适用于我们OpenSSL的后端;其他我们用到的TLS库没有提供所需的API或未提供所需的完整信息。
TLS 1.3及更高版本
这部分并不仅是谈论Qt 6现有的实现,这与未来的发展也有关。截至目前,归功于我们的OpenSSL后端,Qt 6支持最新的TLS协议版本是1.3(Qt 5也支持TLS 1.3)。我们也努力在Schannel后端中启用TLS 1.3。不幸的是,苹果已弃用SecureTransport且没有替代的TLS库,只有一些构建在BoringSSL(名字有点讽刺)之上的更高级别框架。这意味着将来我们将为Darwin用户提供替代方案。至于QNetworkAccessManager,在苹果类(如NSUrlSession)上实现的新QNetworkAccessBackend可能非常方便(附带TLS 1.3和实验性HTTP/3支持!)。
感谢您阅读此博客文章。不要犹豫,并分享您的意见,评论或提出改进建议!