众所周知,HTTP2是使用二进制来传输数据的,相比于HTTP/1.1的明文传输可读性很差,并且大部分浏览器只支持加密的HTTP2,所以对于大部分介绍先基于HTTP/1.1再通过Upgrade头进行升级的文章你基本可以不读下去了。
HTTP/1.1与HTTP2的对比
HTTP/1.1仍在被绝大部分网站支持着,且在未来3年内不会被淘汰(猜测)
HTTP/1.1的长连接能有效的减少多文件请求时的延迟,但是服务器返回的数据必须要按照客户端请求的数据进行响应,否则数据会窜台。
相比于HTTP/1.1,HTTP2使用多路复用,简单来说就是定义一个最小的数据单位——帧,各个帧在同一连接上传输,所以不同文件的数据可以交叉传输,但是也是无法发送文件数据的顺序(即发送一个文件你无法先发送尾部再发送头部,必须由头至尾)
HTTP/1.1的请求头响应头在有时Cookie数据量大的时候占用甚至比响应的数据还要大,所以HTTP2使用了头部压缩(HPack)对头部进行压缩,当多个请求头或响应头出现相同的信息时占用明显减少。
连接
连接阶段,对于HTTP/1.1直接连接交换数据即可,而HTTP/2根据网络你们也能看到通过Upgrade头进行升级的例子,但对于直接进行HTTPS连接的HTTP2,双方在握手阶段就会直接交换所支持的协议
大部分都是在ALPN中设置(Python):
sslsocket.set_alpn_protocols(["h2", "http/1.1"])
Code language: CSS (css)
也能从中看到,HTTP2的简写就是h2。
建立连接之后(握手之后),可以通过
sslsocket.selected_alpn_protocol()
Code language: CSS (css)
来获取客户端选择使用的协议。
自此,连接阶段结束。
数据传输
在进行正式数据传输前,客户端会先发送一个Magic帧,大小为24字节,其内容为
PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
其中PRISM对应着棱镜门事件,指的是美国政府的一个监听群众获取群众隐私的计划,其中包括微软雅虎在内的九家公司,这里不再叙述。
由于这是一个HTTP/1.1格式的请求,所以当服务器不支持HTTP2时会处理该错误,以防止进行传输后才发现对方不响应的尴尬事件。
随后便开始传输数据。
数据传输 – 帧
帧真的是HTTP2一奇妙的设计,帧由9个字节的帧头和负载部分组成,二者为一个整体。
其中帧头里面包含的内容:
- 3字节 —— 负载长度 (Length)
- 1字节 —— 帧类型 (Type)
- 1字节 —— 帧标识 (Flags)
- 4字节 —— 1比特的R标记 + 31比特的流标识
可能有人还不明白比特和字节的关系,一个字节由8个比特组成,比特只有0和1,按照字节储存方式的不同还有字节序(倒着存储字节或正着存储)等。
负载长度告诉了接收者除了读取这9字节的帧头还需要读取多长的数据,帧类型告诉了接收者这是什么帧,帧标识用于标记信息,流标识告诉了这是属于哪个流上的数据,使数据不会窜台。
接下来讲讲帧类型:
- 0 – Data帧,用于传输数据
- 1 – Header帧,用于传输请求头和响应头
- 2 – Priority帧,用于告诉流的优先级
- 3 – Rst_Stream帧,用于告诉服务器或客户端请求取消,别在这个流上传输数据
- 4 – Setting帧,用于服务器与客户端之间交换设定(例如帧最大大小)
- 5 – Push_Promise帧,服务器推送数据(即客户端没有请求但是服务器还是先发过来)
- 6 – Ping帧,用于长时间不传输数据后询问对方是否还保持着连接
- 7 – GoAway帧,用于关闭连接(其中附带原因和错误信息)
- 8 – Window_Update帧,用于实现流量控制
- 9 – Continuation帧,用于对头部的附加
共10个帧类型,其中真正常用的只有Data帧,Header帧,Rst_Stream帧,Setting帧,GoAway帧,Window_Update帧
帧类型等到下次再进行详解(从数据层面提供例子详细解说,而不是单纯给你个框架)
数据传输 – 流
流用于区分帧,可以想象为帧在流上跑,流组成了一个通道(连接)。
其中最特殊的流就是流标识为0的流(下面简称流0)
流0是其他流的爹,包括流量窗口也必须先过流0这一关,流0地位差不多就相当于整个连接。
流0通常用于传输Setting帧、GoAway帧、Ping帧、Window_Update帧,其他帧基本不会在流0上传输。
如何新建一个流?只需在发送Header帧时设置好流标识(随便设)即可,然后数据就会标记上相应的流标识。
数据传输 – 流量控制
流量控制用于控制服务器或客户端负载,流量控制由Window_Update帧实现,且只对Data帧生效(即其他帧不受流量窗口控制)。流量控制不包括帧头。
流量窗口就是一个数字,按照Data帧传输的数据量和发送端负载情况进行减少或增加。
假设流0的流量窗口为16384(字节),流1的流量窗口也是16384,当服务器传输给客户端1000字节大小的Data帧时流0和流1的流量窗口都会减去1000
当流0的流量窗口为0的时候所有流都无法传输数据,即使其他流还有流量窗口
当其他流的流量窗口为0且流0的流量窗口不为0时,那些流量窗口为0的流可以继续传输数据,但是流量窗口为0的流无法传输数据。
所有流传输的Data帧大小都会加在一起在流0上扣除,流0代表的即整个连接。
当流量窗口为0的时候,需要等待客户端/服务器发送Window_Update帧来更新流量窗口大小才可继续发送数据。
等待更新