深入探究Qt HTTP的内部构架

2023-11-16

一、前言

当今互联网时代中,B/S(Browser/Server , 浏览器/服务器)以及C/S(Client/Server , 客户端/服务器)架构已经是绝对的主流软件架构设计方式(除了极少部分的单机软件),它们各有优缺点,这里我们不展开讨论。但是B/S以及C/S架构软件都会有Server,也就是B/S或者C/S中的S,无论是Browser还是Client都必须与Server进行数据交互、传输,比如浏览器浏览网页,客户端注册、登录,获取数据,保存数据等等。那么在他们之间是怎么传输的呢?有什么方法保证数据不丢失,保证数据的完整一致性呢?这就是当今互联网中使用最广泛的网络传输协议HTTP即超文本传输协议(Hypertext transfer protocol)。今天我们就来深入探究一下QT中的HTTP的内部架构。

二、HTTP 协议

2.1 简介
  1. HTTP协议,即超文本传输协议(Hypertext transfer protocol)。是一种详细规定了浏览器和万维网(WWW = World Wide Web)服务器之间互相通信的规则,通过因特网传送万维网文档的数据传送协议。
  2. HTTP协议作为TCP/IP模型中应用层的协议也不例外。HTTP协议通常承载于TCP协议之上,有时也承载于TLS或SSL协议层之上,这个时候,就成了我们常说的HTTPS。
    在这里插入图片描述
  3. HTTP是一个应用层协议,由请求和响应构成,是一个标准的客户端服务器模型。HTTP是一个无状态的协议。
  4. HTTP默认的端口号为80,HTTPS的端口号为443。
  5. 浏览网页是HTTP的主要应用,但是这并不代表HTTP就只能应用于网页的浏览。HTTP是一种协议,只要通信的双方都遵守这个协议,HTTP就能有用武之地。
2.2 特点

1、简单快速:客户向服务器请求服务时,只需传送请求方法和路径。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。

2、灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。

3、HTTP 0.9和1.0使用非持续连接:限制每次连接只处理一个请求,服务器处理完客户的请求,并收到客户的应答后,即断开连接。HTTP 1.1使用持续连接:不必为每个web对象创建一个新的连接,一个连接可以传送多个对象,采用这种方式可以节省传输时间。

4、无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。

5、支持B/S及C/S模式。

三、QT中的HTTP组件

3.1 HTTP组件

HTTP组件是Qt中所有HTTP通信的基础,例如被用于Qt Webkit中。在Qt 5中,HTTP实现中有相当部分被重写,其中大部分的工作是woboq的Markus完成的。下面我们来分析HTTP组件的内部结构,出于简化的目的,我们将分析最主要的类,一些非主要的类,我们会省略掉。

对于QT提供的网络控件,我们可以用如下的方法使用QT提供的网络服务(HTTP)接口:

QUrl url("http://www.baidu.com");
QNetworkRequest request(url);
QNetworkAccessManager manager;
QNetworkReply *reply = manager.get(request);
QObject::connect(reply, SIGNAL(finished()), className, SLOT(replyFinished()));
3.2 网络访问的用户接口

从上面我们的例子和Qt的帮助文档中可以了解到http部分对用户开放的接口情况,QNetworkAccessManager、QNetworkRequest、QNetworkReply三个类包装在Qt上完成http通信所需要使用的接口,简单的来讲:

  1. QNetworkAccessManager : 负责请求的创建以及状态管理
createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *outgoingData)

返回一个QNetworkReply 对象来处理网络请求,参数outgoingData是put/post需要上传的数据,传入QJson类型需要转为QByteArray,在put/post内部转为QBuffer(输出流对象)再传给createRequest,默认情况下会调用QNetworkCookieJar::cookiesForUrl()来保存请求的信息。
对于http/https 的创建方式是这样的:

    // Since Qt 5 we use the new QNetworkReplyHttpImpl
    if (scheme == QLatin1String("http") || scheme == QLatin1String("preconnect-http")
        || scheme == QLatin1String("https") || scheme == QLatin1String("preconnect-https")) {

        QNetworkReplyHttpImpl *reply = new QNetworkReplyHttpImpl(this, request, op, outgoingData);
        connect(this, SIGNAL(networkSessionConnected()),
                reply, SLOT(_q_networkSessionConnected()));
        return reply;
    }

对于其他协议,我们这里就不再展开来讲。

  1. QNetworkRequest : 负责包装http请求,包括设置协议头、解析协议头

  2. QNetworkReply : 负责接收对应请求的返回信息
    对于网络结果返回的实现类是这样的:

QHttpThreadDelegate *delegate = new QHttpThreadDelegate;
    // deleteLater 可以安全的删除delegate
    QObject::connect(thread, SIGNAL(finished()), delegate, SLOT(deleteLater()));
        //通信状态以及数据交互
        ...
        // 用于开始请求.
        QObject::connect(q, SIGNAL(startHttpRequest()), delegate, SLOT(startRequest()));
        QObject::connect(q, SIGNAL(abortHttpRequest()), delegate, SLOT(abortRequest()));

        // 控制连接的缓冲区
        QObject::connect(q, SIGNAL(readBufferSizeChanged(qint64)), delegate, SLOT(readBufferSizeChanged(qint64)));
        QObject::connect(q, SIGNAL(readBufferFreed(qint64)), delegate, SLOT(readBufferFreed(qint64)));

    // 移动 delegate 到 http thread
    delegate->moveToThread(thread);
    //转入子线程处理请求
    emit q->startHttpRequest();

其中 QNetworkAccessManager类,是我们在实现网络中必用的一个类,也是QT中要进行网络访问、传输所面向用户最前列的类。所有的网络访问都是以QNetworkAccessManager开始的。所以我们也是从QNetworkAccessManager类开始分析的。当进行一个HTTP请求时,会先创建一个HTTP网络请求实现的实例,即QNetworkRelpyHttpmpl;
HTTP网络请求的对象包含一个网络请求返回QNetWorkReply与一个HTTP请求的对象QNetworkRequst。
QNetworkReply是hold网络请求返回结果的,我们也需要连接它的槽函数,来进行接受网络返回(不管同步,还是异步,都是通过QNetworkReply来接受HTTP网络请求结果)。

说完上面这些,我们对上面的例子进行解析一下过程网络访问中使用到的主要接口:使用QUrl来创建被用来表示一个HTTP请求的QNetworkRequest。该请求被传递给QNetworkAccessManager,该类负责在网络上发送请求,并返回一个表示HTTP响应的QNetworkReply。根据URL所采用协议的不同,QNetworkAccessManager会创建QNetworkReply的不同内部子类;如果URL采用了“http://”或者“https://”协议,则会创建一个QNetworkReplyHttpImpl实例。该类是用来设置请求,并在发送请求前向其添加例如缓冲或者cookie等信息。
如果该请求被用作上传数据(例如使用HTTP POST或者PUT),该实现类会用到QNonContiguousByteDevice。这个非连续字节类能够用来在不执行memcpy操作的环境中读取文件、字节等。
所以,我们来总结一下这些接口的执行顺序,可用如下的UML图表示(其中蓝色背景表示QT提供给用户可使用的类,也就是公用类。绿色表示为实现公用类定义的私有类,下同):
在这里插入图片描述

3.3 HTTP 工作线程

在Qt 4.8中引入一个特性是多线程的HTTP后端:这个新的后端在一个单独的线程中收发数据,并进行HTTP消息解析。

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

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

该Delegate提供了用来组建HTTP请求和响应的类,名为QHttpNetworkRequest和QHttpNetworkReply。可能这个时候会有小伙伴感到疑惑,因为我们已经拥有了公开接口QNetworkRequest和QNetworkReply;这两个公开接口提供了大量HTTP特有属性的访问接口,例如设置HTTP流水线、状态码和其他HTTP头。而这两个内部类QHttpNetworkRequest和QHttpNetworkRply则用以解析从socket数据流中接受到的HTTP消息,以填充HTTP头和实体。

我们来进一步解析一下HTTP工作线程:
在这里插入图片描述
将HTTP工作线程与主线程分开,将所有的HTTP请求放在一个单独的线程里执行,好处是显而易见的,不仅不会影响主进程,还会提高HTTP的工作效率。

3.4 更底层

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

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

现在,HTTP内部结构的UML图已经基本完整了:
在这里插入图片描述

3.5 其他

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

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

3.6 其他

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

  • Bearer类:用于更加细粒度的调节连接设置等,除了前面提到的QNetworkSession外还有几个类被用到。

  • Cookies:显然,Qt HTTP默认支持cookie的解析和发送,请参见
    QNetworkCookie和QNetworkCookieJar

  • HTTP缓存:尽管Qt也支持HTTP缓存,但未被默认启用。

  • 代理服务器:Qt支持在HTTP和SOCKS5代理服务器上进行网络传输。

  • 上传数据:一些关于上传数据,以及处理HTTP multipart消息的类被省略了。

四、写在最后

在Qt中HTTP作为重要的 模块之一,在Qt4与Qt5分别作了很大的改动。我们现在普遍使用的为Qt5,所以本文中的源码皆以Qt5.7.0 版本中的源码作为分析依据,Qt5对比之前Qt4版本的源码发生了比较大的改变,Qt5中http/https与其他的协议(ftp、spdy等)的实现分离,http2.0也是单独实现,http完全从backend的抽象工厂模式中剥离出来,新的http采用外观模式将http部分的实现隐藏,在源码内部仅暴露出QHttpThreadDelegate,通过QHttpThreadDelegate与QNetworkReplyHttpImpl之间跨线程的信号槽来实现通信以及数据交互。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

深入探究Qt HTTP的内部构架 的相关文章

随机推荐

  • 重装win8.1搜索不到 wifi

    这几天一直忙着研究装系统 毕竟自己是个小白 经常搞到深夜4点钟 今天终于算是有点眉目了 重新装完win8 1 电脑竟然搜索不到wifi 点开右下角那个图标 只有宽带连接这一个选项 于是到网上搜索 怎么解决 网上给了很多答案 基本都差不多 我
  • TCP/IP的三次握手、四次挥手

    本文通过图来梳理TCP IP协议相关知识 TCP通信过程包括三个步骤 建立TCP连接通道 传输数据 断开TCP连接通道 如图1所示 给出了TCP通信过程的示意图 上图主要包括三部分 建立连接 传输数据 断开连接 建立TCP连接很简单 通过三
  • Temporary failure in name resolution解决方法

    终端运行sudo su 输密码 vi etc reslov conf 输入i进入编辑模式 在文档末尾加入 nameserver 8 8 8 8 nameserver 114 114 114 114 按esc 输入 wq保存退出 执行 etc
  • 第八章 Oracle恢复内部原理(重置日志RESETLOGS)

    重置日志选项用于下列情形后的第一次打开数据库的时候 不完全恢复 基于备份控制文件的恢复 CREATE CONTROLFILE RESETLOGS 重置日志的最主要的作用就是丢弃不完全恢复中没有使用的重做日志并保证后续的恢复不再需要 为此 重
  • dll修复工具哪个比较好?修复工具介绍

    DLL 动态链接库 是Windows操作系统中非常重要的一部分 它们存储了各种软件应用程序所需的公共代码和数据 然而 随着时间的推移 电脑上的DLL文件可能会因为各种原因而损坏或丢失 导致系统出现错误 因此 修复DLL错误是一项非常重要的任
  • linux 下 C++ 与三菱PLC 通过MC Qna3E 二进制 协议进行交互

    西门子plc 有snap7库 进行交互 并且支持c 而且跨平台 但是三菱系列PLC并没有现成的开源项目 没办法只能自己拼接 我这里实现了MC 协议 Qna3E 帧 并使用二进制进行交互 pragma once include
  • HDU--1236:排名 (水题)

    1 题目源地址 http acm hdu edu cn showproblem php pid 1236 2 程序源代码 HOJ 1236 排名 include
  • 软件测试框架理论知识

    一 软件测试的定义 1 软件测试的目标应该服从于软件项目的目标 软件测试通过更高效的方法和工具 提高软件开发的效率和质量 2 在规定的条件下 对软件进行审核 运行 评估 验证软件系统是否满足需求规格说明书 3 预防 发现 跟踪软件缺陷 提高
  • Unity篇:加快unity导入工程速度-Cache server链接

    1 已有unity打开 编辑窗口选择点击edit edit下拉展示的列表里打开Preferences 2 选择Cache server界面 Cache server Mode 选择remote 举例 输入192 168 15 131或者19
  • 目标检测中的mAP(mean Average Precision)快速理解

    前言 最近开始接触目标检测 object detection 但是对于衡量算法好坏的mAP Mean Average Precision 并不太理解 经过了一番整理 下面我们就来看看什么是mAP 在目标检测算法中 像是Faster R CN
  • vue上下滑动(仿高德)

    效果图 需求 一个页面底部对应一块区域 底部的区域可以上下滑动到顶部 可以来回上下滑动延伸 如上所述 代码
  • C++实现行列式的相关操作

    目录 一 前言 二 行列式运算操作集 1 概览 2 行列式的定义 3 行列式的输出与输入 4 行列式行与行 列与列的相加 5 行列式的行交换与列交换 6 行列式的行提取公因数与列提取公因数 7 行列式系数清零恢复 8 判断一个行列式是否是三
  • 无须SMTP服务器中转直接将电子邮件发送到对方邮箱

    前言 大家一定熟悉Foxmail中的 特快专递 它能直接将电子邮件发送到对方的邮件服务器中 而不需要经过SMTP服务器中转 这样做有什么好处 第一 发送速度比较快 不需要等SMTP服务器对邮件进行查毒 派发 验证 第二 你可以及时掌握邮件是
  • Spring(一)之控制反转和依赖注入

    1 sts下载 https spring io tools3 sts all 参考博客 https blog csdn net m0 37920381 article details 79972438 2 简介 spring是一个开源框架
  • JQuery针对select下拉框的各项操作

    JQuery针对select下拉框的各项操作 select id change function code 为Select添加事件 当选择其中一项时触发 var checkText select id find option selecte
  • union(联合)注入和布尔注入

    没有很快乐 也没有不快乐 好像不该这样 但也只能这样 成长也许如此 行于奔溃边缘又慢慢自愈吧 网易云热评 一 union联合注入 1 select 1 2 3会生成一张临时表 表中的字段为查询的字段 内容也是查询的字段 2 select 1
  • 什么是Chat GPT?我们能用它来干啥?

    Chat GPT是一款基于人工智能技术的自然语言处理模型 由OpenAI团队开发 它能够通过机器学习技术从海量文本数据中学习语言知识 实现自然语言生成 对话生成和语言理解等功能 使得机器能够更加智能地理解和使用自然语言 Chat GPT的应
  • C++中的异常处理(一)

    异常就是运行时出现的不正常 例如运行时耗尽了内存或遇到意外的非法输入 异常存在于程序的正常功能之外 并要求程序立即处理 不能不处理异常 异常是足够重要的 使程序不能继续正常执行的事件 如果找不到匹配的catch 程序就调用库函数termin
  • OnGUI一些方法使用

    在OnGUI中有很多基本的UI组件 接下来我来为大家介绍一下这些组件的用法与使用 首先是在OnGUI中的两种布局方法 第一种GUILayout BeginVertical 这个方法就是在编辑菜单打开面板的时候对面板的一个纵向的设置 然而又开
  • 深入探究Qt HTTP的内部构架

    一 前言 当今互联网时代中 B S Browser Server 浏览器 服务器 以及C S Client Server 客户端 服务器 架构已经是绝对的主流软件架构设计方式 除了极少部分的单机软件 它们各有优缺点 这里我们不展开讨论 但是