Hyper Text Transfer Protocol(超文本传输协议),是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。是互联网上应用最为广泛的一种网络协议。所有的 WWW 文件都必须遵守这个标准。
超文本传输 协议(HTTP)是用于传输诸如 HTML 的超媒体文档的应用层协议。它被设计用于 Web 浏览器和 Web 服务器之间的通信,但它也可以用于其他目的。 HTTP 是一个基于 TCP/IP 通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。也要通过三次握手,四次挥手。
HTTP 是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。它于 1990 年提出,经过几年的使用与发展,得到不断地完善和扩展。
HTTP 协议工作于客户端-服务端架构为上。浏览器作为 HTTP 客户端通过 URL 向 HTTP 服务端即 WEB 服务器发送所有请求。Web 服务器根据接收到的请求后,向客户端发送响应信息。
大致版本可以分为以下四个:
发展的历史如下:
HTTP 是基于 TCP/IP 协议的应用层协议。它不涉及数据包(packet)传输,主要规定了客户端和服务器之间的通信格式,默认使用 80 端口。 最早版本是 1991 年发布的 0.9 版。该版本极其简单,只有一个命令 GET。
GET /index.html
注意:HTTP 当前运行在 TCP 上,但也可以运行在任何面向连接的服务上。
上面命令表示,TCP 连接(connection)建立后,客户端向服务器请求(request)网页 index.html。
协议规定,服务端收到请求后返回一个以 ASCII 字符流编码的 HTML 文档,不能回应别的格式。
Hello World
服务器发送完毕,就关闭 TCP 连接。
HTTP/0.9 虽然简单,但是它充分验证了 Web 服务的可行性:
把简单的系统变复杂,要比把复杂的系统变简单容易得多。
随着互联网的发展以及浏览器的出现,单纯的文本内容已经无法满足用户需求了,浏览器希望通过 HTTP 来传输脚本、样式、图片、音频和视频等不同类型的文件,所以在 1996 年 HTTP 更新的 1.0 版本中引入了如下特性:
其中最核心的改变是增加了头部设定,头部内容以键值对的形式设置。请求头部通过 Accept 字段来告诉服务端可以接收的文件类型,响应头部再通过 Content-Type 字段来告诉浏览器返回文件的类型。
头部字段不仅用于解决不同类型文件传输的问题,也可以实现其他很多功能如缓存、认证信息等,这些更新内使得它逐渐发展成一种可扩展且灵活的通用协议
TCP 连接需要三个请求才能建立连接,四个请求可以完全关闭
人们创建了一个“连接”标头来解决这个问题。客户端发送带有“connection:keep-alive
”标头的请求,以表明意图为后续请求保持 TCP 连接的打开状态。如果服务器理解此标头并同意遵守该标头,则其响应还将包含“connection:keep-alive
”标头。
这样,双方都保持 TCP 通道打开并使用它进行后续通信,直到任何一方决定关闭它为止。随着 SSL/TLS 加密技术的发展,这一点变得更加重要,因为协商加密算法和交换加密密钥需要在每个连接上增加一个请求/响应周期。
单个 TCP 连接可以通过“connection:keep-alive”标头重用于多个请求
当时,许多 HTTP 改进都是自发出现的。当流行的浏览器或服务器应用程序需要新的 HTTP 功能时,它们会自己实现该功能,并希望其他各方也能效仿。具有讽刺意味的是,去中心化的 Web 需要一个中心化的管理机构来避免碎片化造成的不兼容问题。
该协议的最初创建者蒂姆·伯纳斯·李(TimBerners-Lee)意识到了这种危险,并于 1994 年成立了万维网联盟(W3C),该联盟与互联网工程任务组(IETF)一起致力于规范互联网的技术栈。作为为已有环境带来更多规范的第一步,他们记录了当时 HTTP 中最常用的一些功能,并将其命名为 HTTP/1.0 协议。
但是,由于这种“规范”描述的是多种多样的,通常在“实践”中用法不一致的技术,因此它从未获得过标准地位。相比之下,关于 HTTP 协议新版本的工作已经开始了。
随着互联网的迅速发展,HTTP/1.0 也已经无法满足需求,最核心的就是连接问题。
具体来说就是 HTTP/1.0 每进行一次通信,都需要经历建立连接、传输数据和断开连接三个阶段。当一个页面引用了较多的外部文件时,这个建立连接和断开连接的过程就会增加大量网络开销。
1997 年 1 月,HTTP/1.1 版本发布,只比 1.0 版本晚了半年。它进一步完善了 HTTP 协议,一直用到了 20 年后的今天,直到现在还是最流行的版本。
为了解决 HTTP/1.0 的问题,1999 年推出的 HTTP/1.1 有以下特点:
长连接: HTTP 1.1 支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个 TCP 连接上可以传送多个 HTTP 请求和响应,减少了建立和关闭连接的消耗和延迟,在 HTTP1.1 中默认开启 Connection: keep-alive,一定程度上弥补了 HTTP1.0 每次请求都要创建连接的缺点。
**缓存处理:**在 HTTP1.0 中主要使用 header 里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1 则引入了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略。
带宽优化及网络连接的使用,HTTP1.0 中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1 则在请求头引入了range 头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。
错误通知的管理,在 HTTP1.1 中新增了24 个错误状态响应码,如 409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
Host 头处理,在 HTTP1.0 中认为每台服务器都绑定一个唯一的 IP 地址,因此,请求消息中的 URL 并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个 IP 地址。HTTP1.1 的请求消息和响应消息都应支持 Host 头域,且请求消息中如果没有 Host 头域会报告一个错误(400 Bad Request)。
HTTP/1.1 修复了 HTTP/1.0 的不一致之处,并调整了协议,使其在新的 Web 生态系统中具备更好的性能表现。新版引入的两个最关键的更改是默认使用持久 TCP 连接(保持活动状态)和 HTTP 管线化。
HTTP 管线化的意思就是客户端无需在发送后续 HTTP 请求之前等待服务器响应请求。此功能可以更有效地利用带宽并减少延迟,但它的改进空间甚至更大。HTTP 管线化仍要求服务器按照接收到的请求顺序进行响应,因此,如果管线化中的单个请求执行得很慢,则对客户端的所有后续响应都将相应地延迟下去。这个问题被称为线头阻塞。
HTTP/1.1 与 HTTP/1.0 的一个重要区别是:
HTTP/1.1 是一个“正式的标准”
此后互联网上所有的浏览器、服务器、网关、代理等,只要用到 HTTP 协议,就必须严格遵守这个标准。
存在一些问题如下:
2009 年,谷歌公开了自行研发的 SPDY 协议,主要解决 HTTP/1.1 效率不高的问题。 这个协议在 Chrome 浏览器上证明可行以后,就被当作 HTTP/2 的基础,主要特性都在 HTTP/2 之中得到继承。SPDY 可以说是综合了 HTTPS 和 HTTP 两者有点于一体的传输协议,主要解决:
SPDY 构成图:
谷歌在 2008 年发布了 Chrome 浏览器,这种浏览器因其快速和创新而迅速流行。它使谷歌在互联网技术问题上获得了强大的话语权。在 2010 年代初期,谷歌在 Chrome 中增加了对其 Web 协议 SPDY 的支持。
HTTP/2 标准基于 SPDY,并进行了一些改进。HTTP/2 通过在单个打开的 TCP 连接上多路复用 HTTP 请求,解决了线头阻塞问题。这允许服务器以任何顺序响应请求,然后客户端可以在接收到响应时重新组合响应,从而在单个连接中加快整个交换的速度。
HTTP/2 可以说是 SPDY 的升级版(其实原本也是基于 SPDY 设计的),但是,HTTP2.0 跟 SPDY 仍有不同的地方,主要是以下两点:
先理解几个概念:
HTTP/2 采用二进制格式传输数据,而非 HTTP 1.x 的文本格式,二进制协议解析起来更高效。 HTTP / 1 的请求和响应报文,都是由起始行,首部和实体正文(可选)组成,各部分之间以文本换行符分隔。HTTP/2 将请求和响应数据分割为更小的帧,并且它们采用二进制编码。
每个数据流都以消息的形式发送,而消息又由一个或多个帧组成。 帧是流中的数据单位。一个数据报的 header 帧可以分成多个 header 帧,data 帧可以分成多个 data 帧。
多路复用允许同时通过单一的 HTTP/2 连接发起多重的请求-响应消息。即连接共享,即每一个 request 都是是用作连接共享机制的。一个 request 对应一个 id,这样一个连接上可以有多个 request,每个连接的 request 可以随机的混杂在一起,接收方可以根据 request 的 id 将 request 再归属到各自不同的服务端请求里面。 多路复用原理图:
HTTP1.x 的 header 带有大量信息,而且每次都要重复发送,HTTP/2 使用 encoder 来减少需要传输的 header 大小,通讯双方各自cache 一份 header fields 表,既避免了重复 header 的传输,又减小了需要传输的大小。 为了减少这块的资源消耗并提升性能, HTTP/2 对这些首部采取了压缩策略:
两次请求不相同的 header,传说的 header 如下图所示:
Server Push 即服务端能通过 push 的方式将客户端需要的内容预先推送过去,也叫“cache push”。 服务器可以对一个客户端请求发送多个响应。服务器向客户端推送资源无需客户端明确地请求,服务端可以提前给客户端推送必要的资源,这样可以减少请求延迟时间,例如服务端可以主动把 JS 和 CSS 文件推送给客户端,而不是等到 HTML 解析到资源时发送请求,大致过程如下图所示:
注意: 所有推送的资源都遵守同源策略。 服务器必须遵循请求- 响应的循环,只能借着对请求的响应推送资源。
HTTP/2.0 虽然已经发布了 6 年,不过由于 HTTP/1.1 实在太过经典和强势,目前 HTTP/2.0 的普及率还比较低,仍然有很多网站使用的是 HTTP/1.1 版本。
实际上,使用 HTTP/2 服务器甚至可以在请求之前就将资源提供给客户端!举个例子,如果服务器知道客户端很可能需要样式表来显示 HTML 页面,它可以将 CSS“推”到客户端,而无需等待相应的请求。虽然这从理论上讲是有益的,但此功能在实践中很少见,因为它需要服务器了解其服务的 HTML 结构,但这种情况很少发生。
除了请求正文以外,HTTP/2 还允许压缩请求标头,这进一步减少了通过网络传输的数据量。
HTTP/2 解决了 Web 上的许多问题,但不是全部。在 TCP 协议级别上仍然存在类似类型的线头问题,而 TCP 仍然是 Web 的基础构建块。当 TCP 数据包在传输过程中丢失时,在服务器重新发送丢失的数据包之前,接收方无法确认传入的数据包。由于 TCP 在设计上不遵循 HTTP 之类的高级协议,因此单个丢失的数据包将阻塞所有进行中的 HTTP 请求的流,直到重新发送丢失的数据为止。这个问题在不可靠的连接上尤为突出,这在无处不在的移动设备时代并不罕见。
在 HTTP/2 还处于草案之时,Google 又发明了一个新的协议,叫做 QUIC,继续在 Chrome 和自家服务器里应用,依托它的庞大用户量和数据量,持续地推动 QUIC 协议成为互联网上的标准。
在 2018 年,互联网标准化组织 IETF 提议将“HTTP over QUIC”更名 为“HTTP/3”并获得批准,HTTP/3 正式进入了标准化制订阶段,也许两三年后就会正式发布,到时候我们很可能会跳过 HTTP/2 直接进入 HTTP/3。
由于 HTTP/2 的问题不能仅靠应用程序层来解决,因此协议的新迭代必须更新传输层。但是,创建新的传输层协议并非易事。传输协议需要硬件供应商的支持,并且需要大多数网络运营商的部署才能普及。由于此事涉及的成本和工作量,运营商们不愿进行更新。以 IPv6 为例:它是 24 年前推出的,但如今距离获得普遍支持还有很远的距离。
幸运的是还有另一种选择。UDP 协议与 TCP 一样得到广泛支持,但前者足够简单,可以作为在其之上运行的自定义协议的基础。UDP 数据包是一劳永逸的:没有握手、持久连接或错误校正。HTTP3 背后的主要思想是放弃 TCP,转而使用基于 UDP 的 QUIC 协议。QUIC 以对 Web 环境有意义的方式添加了许多必要的功能(包括以前由 TCP 提供的功能,以及更多功能)。
与 HTTP2 在技术上允许未加密的通信不同,QUIC 严格要求加密后才能建立连接。此外,加密不仅适用于 HTTP 负载,还适用于流经连接的所有数据,从而避免了一大堆安全问题。建立持久连接、协商加密协议,甚至发送第一批数据都被合并到 QUIC 中的单个请求/响应周期中,从而大大减少了连接等待时间。如果客户端具有本地缓存的密码参数,则可以通过简化的握手(0-RTT)重新建立与已知主机的连接。
为了解决传输级别的线头阻塞问题,通过 QUIC 连接传输的数据被分为一些流。流是持久性 QUIC 连接中短暂、独立的“子连接”。每个流都处理自己的错误纠正和传递保证,但使用连接全局压缩和加密属性。每个客户端发起的 HTTP 请求都在单独的流上运行,因此丢失数据包不会影响其他流/请求的数据传输。
UDP 是一种无状态协议(持久连接只是其之上的抽象),使 QUIC 能够支持一些很大程度上忽略了数据包传递复杂性的功能。例如,从理论上讲,客户端更改其 IP 地址中间连接(例如智能手机从移动网络跳转到家庭 wifi)时不应中断连接,因为该协议允许在不同 IP 地址之间迁移而无需重新连接。
QUIC 协议的所有现有实现当前都在用户空间,而不是 OS 内核中运行。由于客户端(例如浏览器)和服务器的更新通常比操作系统内核更新的频率更高,因此人们希望可以藉此更快地采用新功能。
TCP 协议已经存在了很长时间,对于路由器来说很容易理解。它具有清晰的未加密标记(用于建立和关闭连接),可用于跟踪和控制现有会话。在网络硬件学会了解新协议之前,它将把 QUIC 流量简单地看作独立的 UDP 数据包流,这将使网络配置更加棘手。
从客户端缓存“恢复”连接的能力使该协议很容易遭受重播攻击:在某些情况下,恶意攻击者可以重新发送以前捕获的数据包,这些数据包将被服务器解释为有效的,来自受害者的。像那些提供静态内容的 Web 服务器一样,许多 Web 服务器不会受到此类攻击的伤害。对于身处易受攻击环境的应用程序来说,必须要记住禁用 0-RTT 功能。
这就是 HTTP 到今天为止的故事。我认为 HTTP/3 是向前迈出的一大步,并且当然希望 HTTP/3 在不久的将来会被广泛采用。
原文链接:The Long Road to HTTP/3 · Scorpil
协议版本 | 解决的核心问题 | 解决方式 |
---|---|---|
0.9 | HTML 文件传输 | 确立了客户端请求、服务端响应的通信流程 |
1.0 | 不同类型文件传输 | 设立头部字段 |
1.1 | 创建/断开 TCP 连接开销大 | 建立长连接进行复用 |
2 | 并发数有限 | 二进制分帧 |
3 | TCP 丢包阻塞 | 采用 UDP 协议 |
主要特点:
参考文章:
Http系列(-) Http发展历史 - 掘金
从HTTP到HTTP/3的发展简史_文化 & 方法_Scorpil_InfoQ精选文章
HTTP 发展史 - 知乎