1 HTTP/0.9
HTTP 的最早版本诞生在1991年,这个最早版本和现在比起来极其简单,没有HTTP 头,没有状态码,没有版本号。后来它的版本号才被定为0.9 ,用来和其他版本的HTTP 区分。HTTP/0.9 只支持一种请求方式—— GET ,请求只有一行:
GET /hello.html
响应也是非常简单的,只包含html 文档本身:
<HTML>
Hello world
</HTML>
当TCP 建立连接之后,服务器向客户端返回HTML 格式的字符串。发送完毕后,就关闭TCP 连接。由于没有状态码和错误代码,如果服务器处理的时候发生错误,只会传回一个特殊的包含问题描述信息的HTML 文件。 这就是最早的HTTP/0.9 版本。
2 HTTP/1.0
1996年,HTTP/1.0 版本发布,大大丰富了HTTP 的传输内容,除了文字,还可以发送图片、视频等,这为互联网的发展奠定了基础。相比HTTP/0.9 ,HTTP/1.0 主要有如下特性:
- 请求与响应支持
HTTP 头,增加了状态码,响应对象的一开始是一个响应状态行; - 协议版本信息需要随着请求一起发送,支持
HEAD ,POST 方法; - 支持传输
HTML 文件以外其他类型的内容;
一个典型的HTTP/1.0 的请求像这样:
GET /hello.html HTTP/1.0
User-Agent:NCSA_Mosaic/2.0(Windows3.1)
200 OK
Date: Tue, 15 Nov 1996 08:12:31 GMT
Server: CERN/3.0 libwww/2.17
Content-Type: text/html
<HTML>
一个包含图片的页面
<IMGSRC="/smile.gif">
</HTML>
3 HTTP/1.1
在HTTP/1.0 发布几个月后,HTTP/1.1 就发布了。HTTP/1.1 更多的是作为对HTTP/1.0 的完善,在HTTP 1.1 中,主要具有如下改进:
- 可以复用连接;
- 增加
pipeline :HTTP 管线化是将多个HTTP 请求整批提交的技术,而在传送过程中不需先等待服务端的回应。管线化机制须通过永久连接(persistent connection )完成。 浏览器将HTTP 请求大批提交可大幅缩短页面的加载时间,特别是在传输延迟(lag/latency )较高的情况下。有一点需要注意的是,只有幂等的请求可以使用pipeline ,如GET ,HEAD 方法。 - chunked 编码传输:该编码将实体分块传送并逐块标明长度,直到长度为0 块表示传输结束, 这在实体长度未知时特别有用(比如由数据库动态产生的数据);- 引入更多缓存控制机制:如
etag ,cache-control - 引入内容协商机制 ,包括语言,编码,类型等,并允许客户端和服务器之间约定以最合适的内容进行交换; - 请求消息和响应消息都支持
Host 头域:在HTTP 1.0 中认为每台服务器都绑定一个唯一的IP 地址,因此,请求消息中的URL 并没有传递主机名(hostname )。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers ),并且它们共享一个IP 地址。因此,Host 头的引入就很有必要了; - 新增了
OPTIONS 、PUT 、 DELETE 、TRACE 、CONNECT 方法;
pipeline [?pa?pla?n](常指地下的)输送管道
虽然HTTP/1.1 已经优化了很多点,作为一个目前使用最广泛的协议版本,已经能够满足很多网络需求,但是随着网页变得越来越复杂,甚至演变成为独立的应用,HTTP/1.1 逐渐暴露出了一些问题:
- 在传输数据时,每次都要重新建立连接,对移动端特别不友好;
- 传输内容是明文,不够安全;
Header 内容过大,每次请求Header 变化不大,造成浪费;keep-alive 给服务端带来性能压力;
为了解决这些问题,HTTPS 和SPDY 应运而生。
4 HTTPS
HTTPS 是以安全为目标的HTTP 通道,简单讲是HTTP 的安全版。HTTPS 的安全基础是SSL ,因此加密的详细内容就需要SSL 。
HTTPS 协议的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种就是确认网站的真实性。 HTTPS 和HTTP 的区别主要如下:
HTTPS 协议使用ca 申请证书,由于免费证书较少,需要一定费用;HTTP 是明文传输,HTTPS 则是具有安全性的SSL 加密传输协议;HTTP 和HTTPS 使用的是完全不同的连接方式,用的端口也不一样,前者是80 ,后者是443 。
5 SPDY
其实SPDY 并不是新的一种协议,而是在HTTP 之前做了一层会话层:
在2010年到2015年,谷歌通过实践一个实验性的SPDY 协议,证明了一个在客户端和服务器端交换数据的另类方式。其收集了浏览器和服务器端的开发者的焦点问题,明确了响应数量的增加和解决复杂的数据传输。
在启动SPDY 这个项目时预设的目标是:
- 页面加载时间(
PLT ))减少50% ; - 无需网站作者修改任何内容;
- 将部署复杂性降至最低,无需变更网络基础设施;与开源社区合作开发这个新协议;
- 收集真实性能数据,验证这个实验性协议是否有效;
为了达到降低目标,减少页面加载时间的目标,SPDY 引入了一个新的二进制分帧数据层,以实现多向请求和响应、优先次序、最小化及消除不必要的网络延迟,目的是更有效地利用底层TCP 连接。
6 HTTP/2.0
2015 年,HTTP/2.0 问世,以下是HTTP/2.0 的特点:
- 使用二进制分帧层:在应用层与传输层之间增加一个二进制分帧层,以此达到在不改动
HTTP 的语义,HTTP 方法、状态码、URI 及首部字段的情况下,突破HTTP1.1 的性能限制,改进传输性能,实现低延迟和高吞吐量。在二进制分帧层上,HTTP2.0 会将所有传输的信息分割为更小的消息和帧,并对它们采用二进制格式的编码,其中HTTP1.x 的首部信息会被封装到Headers 帧,而我们的request body 则封装到Data 帧里面;
- 多路复用:对于
HTTP/1.x ,即使开启了长连接,请求的发送也是串行发送的,在带宽足够的情况下,对带宽的利用率不够,HTTP/2.0 采用了多路复用的方式,可以并行发送多个请求,提高对带宽的利用率;
- 数据流优先级:由于请求可以并发发送了,那么如果出现了浏览器在等待关键的
CSS 或者JS 文件完成对页面的渲染时,服务器却在专注的发送图片资源的情况怎么办呢?HTTP/2.0 对数据流可以设置优先值,这个优先值决定了客户端和服务端处理不同的流采用不同的优先级策略; - 服务端推送:在
HTTP/2.0 中,服务器可以向客户发送请求之外的内容,比如正在请求一个页面时,服务器会把页面相关的logo ,CSS 等文件直接推送到客户端,而不会等到请求来的时候再发送,因为服务器认为客户端会用到这些东西。这相当于在一个HTML 文档内集合了所有的资源; - 头部压缩:使用首部表来跟踪和存储之前发送的键值对,对于相同的内容,不会再每次请求和响应时发送;
HTTP/2.0 的新特点和SPDY 很相似,其实HTTP/2.0 本来就是基于SPDY 设计的,可以说是SPDY 的升级版。 但是HTTP/2.0 仍有和 SPDY 不同的地方,主要有如下两点:
HTTP2.0 支持明文HTTP 传输,而SPDY 强制使用HTTPS ;HTTP2.0 消息头的压缩算法采用HPACK ,而非SPDY 采用的DEFLATE ;
7 Http 的持久连接和HttpClient 连接池
HTTP 协议是无状态的协议,即每一次请求都是互相独立的。因此它的最初实现是,每一个HTTP 请求都会打开一个TCP Socket 连接,当交互完毕后会关闭这个连接。由于HTTP 协议是全双工的协议,所以建立连接与断开连接是要经过三次握手与四次挥手的,在这种设计中,每次发送HTTP 请求都会消耗很多的额外资源,即连接的建立与销毁。
三次握手:
- 首先
Client 端发送连接请求报文; Server 段接受连接后回复ACK 报文(ACK (Acknowledge character)确认字符 ),并为这次连接分配资源;Client 端接收到ACK 报文后也向Server 段发生ACK 报文,并分配资源,这样TCP连接就建立了;
四次挥手(中断连接端可以是Client 端,也可以是Server 端):
- 假设
Client 端发起中断连接请求,也就是发送FIN 报文; Server 端接到FIN 报文后,如果此时还有数据没有发送完成,并不会立刻关闭Socket ,而是继续发送数据。但是可以先发送ACK ,告诉Client 端,请求已收到了,但数据还在发送,请继续等Server 的消息。这个时候Client 端就进入FIN_WAIT 状态,继续等待Server 端的FIN 报文;- 当
Server 端确定数据已发送完成,则向Client 端发送FIN 报文,表示Sever 端的数据已发送完成,准备好关闭连接了; Client 端收到FIN 报文后,就知道可以关闭连接了,但是担心Server 端不知道要关闭,所以发送ACK 后进入TIME_WAIT 状态,如果Server 端没有收到ACK 则可以重传。 Server 端收到ACK 后,就知道可以断开连接了。Client 端等待了2MSL 后依然没有收到回复,则证明Server 端已正常关闭,此时Client 端也可以关闭连接了;
每次建立连接关闭连接都要三次握手、四次挥手,显然会造成效率低下。于是,HTTP 协议的也进行了发展,通过持久连接的方法来进行Socket 连接复用。
从图中可以看到:
- 在串行连接中,每次交互都要打开关闭连接;
- 在持久连接中,第一次交互会打开连接,交互结束后连接并不关闭,下次交互就省去了建立连接的过程;
持久连接的实现有两种:HTTP/1.0+ 的keep-alive 与HTTP/1.1 的持久连接。
7.1 HTTP/1.0+ 的Keep-Alive
从1996年开始,很多HTTP/1.0 浏览器与服务器都对协议进行了扩展,那就是keep-alive 扩展协议。这个扩展协议是作为HTTP/1.0 的补充的“实验型持久连接”出现的。keep-alive 已经不再使用了,最新的HTTP/1.1 规范中也没有对它进行说明,只是很多应用延续了下来。
使用HTTP/1.0 的客户端在首部中加上Connection:Keep-Alive ,请求服务端将一条连接保持在打开状态。服务端如果愿意将这条连接保持在打开状态,就会在响应中包含同样的首部。如果响应中没有包含Connection:Keep-Alive 首部,则客户端会认为服务端不支持keep-alive ,会在发送完响应报文之后关闭掉当前连接。
通过keep-alive 补充协议,客户端与服务器之间完成了持久连接,然而仍然存在着一些问题:
- 在
HTTP/1.0 中keep-alive 不是标准协议,客户端必须发送Connection:Keep-Alive 来激活keep-alive 连接; - 代理服务器可能无法支持
keep-alive ,因为一些代理是"盲中继",无法理解首部的含义,只是将首部逐跳转发。所以可能造成客户端与服务端都保持了连接,但是代理不接受该连接上的数据;
7.2 HTTP/1.1 的持久连接
HTTP/1.1 采取持久连接的方式替代了Keep-Alive 。HTTP/1.1 的连接默认情况下都是持久连接,如果要显式关闭,需要在报文中加上Connection:Close 首部,即在HTTP/1.1 中,所有的连接都进行了复用。 然而如同Keep-Alive 一样,空闲的持久连接也可以随时被客户端与服务端关闭。不发送Connection:Close 不意味着服务器承诺连接永远保持打开。
HttpClient 如何生成持久连接?HttpClient 中使用了连接池来管理持有连接,同一条TCP 链路上,连接是可以复用的。HttpClient 通过连接池的方式进行连接持久化。
其实“池”技术是一种通用的设计,其设计思想并不复杂:
- 当有第一次连接的时候建立连接;
- 结束时对应连接不关闭,归还到池中;
- 下次同个目的的连接可从池中获取一个可用连接;
- 定期清理过期连接;
参考
https://zhuanlan.zhihu.com/p/49949879 https://www.jb51.net/article/141015.htm
|