1.1 存在的问题:
为了兼容1.1,2的思路如下:
1.1 的头部有一些问题:
2 使用了 HPACK 算法压缩头部:
不同索引对应的值是在变化的。如果头部字段属于静态表,且值变化,则前两位编码为 01
。无需\r\n分隔请求头和请求体,采用长度定义每个键值对的边界
根据统计信息,http/2将字符编码为了一张静态 Huffman 编码表,例如:
这显然要比 ASCII 码使用的空间更少。会用1对齐一字节
一个 key-value 压缩实例:
新的头部字段的 key 和 value 都使用 huffman 编码,双方将其添加进自己的动态表中。
通过每个键值对的开头区分:
如果完全匹配,则索引号以1开头,不再发送 value length & value string
对于每个连接,如果动态表过大会影响服务器的性能,所以每个服务器都会限制http2的请求次数,避免动态表过大
每个报文可以分为:头部+数据
状态码200可以从3字节优化到1字节:10001000
2定义了帧的类型:数据帧和控制帧
标志位可以携带简单的控制信息,也即8个标志位,例如:
后面的32位中,1位保留,31位作为流标识符(id),使得接收方可以区分这个帧属于哪个流
再后面就是帧数据,是使用HPACK算法压缩的 HTTP头部和包体
2中有三个概念:Stream、Message、Frame:
流ID有上限,同一个连接中的流id不能重复,只能顺序递增。当达到id上限,需发送一个控制帧 GOAWAY
来关闭 TCP 连接
2可以实现100个并发流只需一次TCP连接,而1想要100个并发只能建立100个TCP连接
流可以设置优先级,例如希望服务器先发送 html 再发送图片,从而提高用户体验
主动建立流,在已建立的流中发送 PUSH_PROMISE
帧,也是HTTP头,它告知客户端接下来在哪个流中发送包体
2基于 TCP 实现,它仍存在一些问题:
队头阻塞:多个流跑在同一个TCP连接中,一个流的帧数据丢失,则整个连接都被阻塞
基于 UDP 的应用层协议,它具有类似 TCP 的连接管理、拥塞窗口、流量控制的网络特性,且是一种可靠 UDP
它的特性有:
基于 QUIC,QUIC 有流的概念,于是3无需再次定义流:
帧头有两个字段:类型和长度,分为数据帧和控制帧;
压缩算法使用 QPACK,它也使用静态表、动态表、Huffman编码,但是静态表扩大到了 91 项。
且解决了 HPACK 的问题:首次通信丢包,动态表未建立导致的重传阻塞。QPACK 使用了两个单向流建立动态表:
RPC:Remote Procedure Call,远程过程调用/远程函数调用,可以屏蔽网络细节调用服务器上的函数
虽然大部分 RPC 协议底层使用 TCP,但实际上它们不一定非得使用 TCP,改用 UDP 或者 HTTP,其实也可以做到类似的功能。
历史上的发展是,TCP–RPC–HTTP,软件内部只需要 RPC 即可通信。而不同公司的服务器架构不同,RPC协议实现也不同,浏览器无法使用一个统一的协议进行通信,所以出现了 HTTP
HTTP 与 RPC 都有服务发现的过程,也可以自行实现连接池进行复用。
1.1 的HTTP使用 JSON 序列化结构体,其中存在大量重复字段,性能不如可定制化、序列化体积更小的 RPC。
改进后的2比很多RPC协议还好,所以又开始用 2 了——2出现的比RPC还晚好多
传统的 HTTP 中,服务器不能进行主动推送,需要靠前端不断轮询实现伪主动推送。这会造成一些问题:
一种解决方案是 长轮询,即留给服务器一定时间做响应,服务器这段时间内接收到请求则立即返回,超时则客户端重新发送查询
而这本质上还是客户端主动取数据,对于扫码登陆可以有效响应,但是如果有大量数据的主动推送则不行了,于是出现了 WebSocket 协议
这是一种全双工协议,双方可以同时主动向对方发送数据;而 HTTP 是半双工,同一时间只有一方可以主动发送数据
TCP连接–HTTP首次通信,若需更换为 WebSocket,则在头部添加下列字段:
若服务器支持WebSocket,则会使用HTTP开始WebSocket握手:
双方都把这个 key 用一个公开算法计算,服务端将其通过 101 协议切换 响应给客户端,客户端比较新字符串是否一致,是则验证通过。
只在建立连接阶段使用 HTTP,后续使用 WebSocket
一个数据包:帧
opcode:数据帧类型,例如字符串/二进制数据/关闭连接帧
payload 长度:先读7bit,根据这7位选择是否要读扩展长度位
服务器和客户端进行频繁交互:网页/小程序游戏,网页聊天室,以及一些类似飞书这样的网页协同办公软件。
例如怪物攻击是服务器逻辑,造成伤害会主动发送给客户端,客户端,客户端展示效果