Skip to main content

Qt HTTP内部构架

Comments

原文链接 Peter Hartmann - Inside the Qt HTTP stack

Qt HTTP组件是Qt中所有HTTP通信的基础,例如被用于Qt Webkit中。在Qt 5中,HTTP实现中有相当部分被重写,其中大部分的工作是woboq的Markus完成的。这篇文章将试图分析HTTP组件的内部结构,注意出于简化的目的,一些类被省略。

我们可以用如下的方法使用公开接口。

简单的例子

QUrl url("http://qt.gitorious.org");
QNetworkRequest request(url);
QNetworkAccessManager manager;
QNetworkReply *reply = manager.get(request);
QObject::connect(reply, SIGNAL(finished()), myClass, SLOT(replyFinished()));

1. 公开接口及其朋友

以上的例子展示了主要的接口:使用QUrl来创建被用来表示一个HTTP请求的QNetworkRequest。该请求被传递给QNetworkAccessManager,该类负责在网络上发送请求,并返回一个表示HTTP响应的QNetworkReply。根据URL所采用协议的不同,QNetworkAccessManager会创建QNetworkReply的不同内部子类;如果URL采用了“http://”或者“https://”协议,则会创建一个QNetworkReplyHttpImpl实例。该类是用来设置请求,并在发送请求前向其添加例如缓冲或者cookie等信息。

如果该请求被用作上传数据(例如使用HTTP POST或者PUT),该实现类会用到QNonContiguousByteDevice。这个非连续字节类能够用来在不执行memcpy操作的环境中读取文件、字节等。

这些接口可用如下的UML图表示:

2. 工作线程

在Qt 4.8中引入一个特性是多线程的HTTP后端:这个新的后端在一个单独的线程中收发数据,并进行HTTP消息解析(更多细节请参见这篇文章)。

QNetworkReplyHttpImpl会创建一个名为QHttpThreadDelegate的类,并将其放置在这个新的线程中(被称为HTTP线程)。这个QHttpThreadDelegate是一个Facade类,为所有在HTTP线程中的操作提供了一个接口。所有在QNetworkReplyHttpImplQHttpThreadDelegate之间的跨线程通信和数据传递都是通过信号和槽完成的。这意味着Delegate提供了一些槽,由HttpImpl发出的信号所触发,反之亦然。

每当QNetworkReplyHttpImpl被创建时,它都会创建一个相应的QHttpThreadDelegate,链接相应的信号和槽,并将该Delegate移动到HTTP线程中去。

该Delegate提供了用来组建HTTP请求和响应的类,名为QHttpNetworkRequestQHttpNetworkReply。这个名字会让人困惑,因为我们已经拥有了公开接口QNetworkRequestQNetworkReply;这两个公开接口提供了大量HTTP特有属性的访问接口,例如设置HTTP流水线、状态码和其他HTTP头。而这两个内部类QHttpNetworkRequestQHttpNetworkRply则用以解析从socket数据流中接受到的HTTP消息,以填充HTTP头和实体。

下图展示了将这几个类添加到类图后的结构。

3. 更底层

HTTP请求和相应是在所谓的“频道”上进行收发的;简单的说,每个“频道”就是一个socket,并附加了一些用于维护HTTP状态和特性的逻辑。对于普通的HTTP请求,通常采用QTcpSocket作为该频道上的socket,而对于“https://”则采用QSslSocket

一系列连接到同一服务器的频道组成一个连接。对于和同一个服务器的通信,这里总是只有一个连接,以及最多同时有六个频道。此外,当HTTP流水线被启用的时候,还可以同时发出更多的请求。当通过socket接收到一个相应时,该socket并不会被自动关闭,而是默认被用作后续请求,从而节省socket的初始化时间并重用一个已经具有更大TCP窗口值的socket。

现在,HTTP内部结构的UML图已经基本完整了:

4. 其他

到目前还有两个重要的类未被提及:

  • QNetworkSession:该类主要用于移动设备之中,特别是在缺少互联网的持续连接的环境中。当缺乏对互联网的连接时,QNetworkSession及其相关类会尝试创建一个连接(例如,程序可以连接QNetworkSession的信号,让用户在3G和Wifi连接之间进行选择)。该类被QNetworkAccessManager初始化。
  • QNetworkAccessAuthenticationManager:该全局类被用于存储能够被重用的认证信息。当服务器要求认证时,QNetworkAccessManager会发出一个信号(QNetworkAccessManager::authenticationRequired()),要求用户输入用户名和密码。而这个认证管理器类则会缓存此信息,并在后续的请求中自动将其发送给服务器。有趣的是,这个认证管理器也使用了QNetworkAccessCache,和QHttpNetworkConnection用来缓存连接一样。

因此,(本文中提及的)Qt HTTP内部构架的完整类图如下所示:

还有哪些未被提及

尽管上面这个类图已经显得够复杂了,但还有一些类和领域被省略了:

  • Bearer类:用于更加细粒度的调节连接设置等,除了前面提到的QNetworkSession外还有几个类被用到。
  • Cookies:显然,Qt HTTP默认支持cookie的解析和发送,请参见QNetworkCookieQNetworkCookieJar
  • HTTP缓存:尽管Qt也支持HTTP缓存,但未被默认启用。
  • 代理服务器:Qt支持在HTTP和SOCKS5代理服务器上进行网络传输。
  • 上传数据:一些关于上传数据,以及处理HTTP multipart消息的类被省略了。

Comments

Subscribe to our blog

Try Qt 6.11 Now!

Download the latest release here: www.qt.io/download

Qt 6.11 is now available, with new features and improvements for application developers and device creators.

We're Hiring

Check out all our open positions here and follow us on Instagram to see what it's like to be #QtPeople.