这是我参与[第三届青训营-后端场]笔记创作活动的第5篇笔记
HTTP1
- 队头阻塞:HTTP1是请求响应的模式,所以如果前一个请求阻塞了,后一个请求也会被阻塞,这是HTTP队头阻塞。
- 传输效率低:如果传输的http报文数据少,容易出现头大体小的问题。
- 明文传输:可读性强,对开发者来说可以高效的进行调试,但是容易泄露。(可以使用TLS加密)
HTTP2
- 队头阻塞:不存在HTTP队头阻塞,但存在TCP队头阻塞。多个请求流复用同一条tcp连接,但接收方仍需要顺序接收,如果前面有报文片段丢失了,那么需等待重传,后续请求也会被阻塞。
- 多路复用:HTTP2增加了一个流的概念,每个请求对应一个流ID,客户端流ID的奇数,服务端流ID是偶数。可以多条流同时存在连接上,不同流的报文可以乱序,同一个流的报文必须顺序串行。
- 头部压缩:HPACK算法。服务端和客户端各自维护索引表。索引表包括静态表和动态表。静态表保存无需更新的头部字段。动态表是随着编解码随时更新的。
如果要头部字段查表查到了,则可以使用索引代替头部字段的key-value,否则需要发送完整的头部字段并更新到索引表中。
- 二进制帧协议:分为头部帧header和数据帧body。 (HTTP2不存在起始行,起始行的版本号,状态码,方法,URI等都变为了伪头部例如:authority,:method,:status)
如图流ID是31bits,而客户端的流ID是奇数,所以一个连接客户端可以发送2^30个请求。如果流ID达到最大值后,可以发送GPAWAY帧,新创建一个TCP连接,流ID重新计数。
此外,可以发送RSTSREAM停止流的发送和接收,但不会断开TCP连接。
流状态转化图
- PushServer:向一个服务器请求一个页面后,服务器可以主动将页面包含的资源也推送给客户端。
HTTP3/QUIC
- 基于UDP实现:选择UDP作为传输层协议,然后把tcp的连接管理,拥塞控制,流量控制,重传等搬过来,实现可靠传输,并且是可拔插的。
- 解决队头阻塞:QUIC存在流的概念,但是如果一个流发送了丢包,不会阻塞到其他流,因为传输层使用的是UDP无连接的协议。
- 加密握手次数减少:将密钥交换的步骤也加入到hello报文中。
TLS1.3+
- 快速启动:无需三次握手,TLS握手最多也只需要3次握手,并且TLS也支持0RTT
(1)客户端:生成随机数 a,选择公开的大数 G 和 P,计算 A=a*G%P,将 A 和 G 发送给服务器,也就是 Client Hello 消息
(2)客户端:客户端直接使用缓存的 ServerConfig 计算通信密钥 initKEY = aB = ab*G%P,加密发送应用数据
(3)服务器:根据 Client Hello 消息计算通信密钥 initKEY = bA = ba*G%P
(4)服务器:生成随机数 c,计算 C=c*G%P,使用 initKey 加密 C,发送给客户端,也就是 Server Hello 消息
(5)客户端:使用 initKey 解码获取 C,计算会话密钥 sessionKey = aC = ac*G%P,加密发送应用数据 2
(6)服务器:计算会话密钥 sessionKey = cA = ca*G%P,解密获取应用数据 2
客户端缓存的 ServerConfig 是服务器静态配置的,是可以长期使用的。客户端通过 ServerConfig 实现 0-RTT 握手,使用会话密钥 sessionKey 保证通信数据的前向安全。
- 可靠传输 QUIC传输单位有包和帧。包面向的是连接,帧面向的是流。多个帧组成一个流,对应一个请求。多个帧被打包成一个包进行传输。
PKN和SACK机制。
与tcp一样,存在seq和ack机制,不同的是,QUIC包的序号会一直递增。
顺序接收:由于QUIC的包是一直递增的,无法通过包序号来进行顺序接收。QUIC在每个帧都有一个字段 stream offset,可以通过这个偏移量来对包进行顺序重组。
流量控制:跟tcp一样通过滑动窗口实现。但滑动窗口分为了connection和stream两个层面的窗口。每个stream都有一个窗口与之对应,只限制单个流,connection是面向整个连接的窗口,限制所有数据流的窗口。
connection可用窗口的大小就是20+30+10=60
拥塞控制,超时重传,快速重传都跟TCP差不多,这里不做赘述。
连接迁移:一个TCP连接是通过四元组标识的,如果IP发生了变化,那么连接就会失效。QUIC使用了一个64位连接ID标识一个连接,这样即使切换了网络改变了IP地址,连接依旧正常。