通信技术|从起源到发展 详说HTTP从1到3的演变( 六 )


如果第一、二、三个的包都丢失了也没有关系,当发送方收到第四个包时,它可以确信一定是前三个 ACK 丢了而不是数据包丢了,否则不会收到 4001 的 ACK,所以发送方可以大胆的把窗口向后滑动四位 。
滑动窗口的概念大幅度提高了 TCP 传输数据时抗干扰的能力,一般丢失一两个 ACK 根本没关系 。但如果是发送的包丢失,或者出错,窗口就无法向前滑动,出现了队头阻塞的现象 。
QUIC 是如何做的
通信技术|从起源到发展 详说HTTP从1到3的演变
文章图片

QUIC 协议基于 UDP 实现,我们知道 UDP 协议只负责发送数据,并不保证数据可达性 。这一方面为 QUIC 的多路复用提供了基础,另一方面也要求 QUIC 协议自己保证数据可达性 。
SPDY 为各个数据包做好标记,指明他们属于哪个 HTTP 请求,至于这些包能不能到达客户端,SPDY 并不关心,因为数据可达性由 TCP 协议保证 。既然客户端一定能收到包,那就只要排序、拼接就行了 。QUIC 协议采用了多路复用的思想,但同时还得自己保证数据的可达性 。
TCP 协议的丢包重传并不是一个好想法,因为一旦有了前后顺序,队头阻塞问题将不可避免 。而无序的数据发送给接受者以后,如何保证不丢包,不错包呢?这看起来是个不可能完成的任务,不过如果把要求降低成:最多丢一个包,或者错一个包 。事情就简单多了,操作系统中有一种存储方式叫 RAID 5,采用的是异或运算加上数据冗余的方式来保证前向纠错(FEC: Forward Error Correcting) 。QUIC 协议也是采用这样的思想,这里不再赘述 。
利用冗余数据的思想,QUIC 协议基本上避免了重发数据的情况 。当然 QUIC 协议还是支持重传的,比如某些非常重要的数据或者丢失两个包的情况 。
少 RTT,请求更快速
前面说到,一次 HTTPS 请求,它的基本流程是三次 TCP 握手外加四次 SSL/TLS 握手 。也就是需要三个 RTT 。但是 QUIC 在某些场景下,甚至能够做到 0RTT 。
首先介绍下什么是 0RTT 。所谓的 0RTT 就是通信双方发起通信连接时,第一个数据包便可以携带有效的业务数据 。而我们知道,这个使用传统的TCP是完全不可能的,除非你使能了 TCP 快速打开特性,而这个很难,因为几乎没人愿意为了这个收益去对操作系统的网络协议栈大动手脚 。未使能 TCP 快速打开特性的TCP传输第一笔数据前,至少要等1个RTT 。
我们这里再说说 HTTP2 。对于 HTTP2 来说,本来需要一个额外的 RTT 来进行协商,判断客户端与服务器是不是都支持 HTTP2,不过好在它可以和 SSL 握手的请求合并 。这也导致了一个现象,就是大多数主流浏览器仅支持 HTTPS2 而不单独支持 HTTP2 。因为 HTTP2 需要一个额外的 RTT,HTTPS2 需要两个额外的 RTT,仅仅是增加一个 RTT 就能获得数据安全性,还是很划算的 。
TCP 快速打开
何谓 TCP 快速打开,即客户端可以在发送第一个 SYN 握手包时携带数据,但是 TCP 协议的实现者不允许将把这个数据包上传给应用层 。这主要是为了防止 TCP 泛洪攻击 [http://techimg88.guangdonglong.com/img.php?https://tools.ietf.org/html/rfc4987] 。
因为如果 SYN 握手的包能被传输到应用层,那么现有的防护措施都无法防御泛洪攻击,而且服务端也会因为这些攻击而耗尽内存和 CPU 。
当然 TCP 快速打开并不是完全不可行的 。人们设计了 TFO (TCP Fast Open),这是对 TCP 的拓展,不仅可以在发送 SYN 时携带数据,还可以保证安全性 。
TFO 设计了一个 Cookie,它在第一次握手时由 server 生成,Cookie 主要是用来标识客户端的身份,以及保存上次会话的配置信息 。因此在后续重新建立 TCP 连接时,客户端会携带 SYN + Cookie + 请求数据,然后不等 ACK 返回就直接开始发送数据 。
通信技术|从起源到发展 详说HTTP从1到3的演变
文章图片

服务端收到 SYN 后会验证 Cookie 是否有效,如果无效则会退回到三次握手的步骤,如下图所示:
通信技术|从起源到发展 详说HTTP从1到3的演变
文章图片

同时,为了安全起见,服务端为每个端口记录了一个值 PendingFastOpenRequests,用来表示有多少请求利用了 TFO,如果超过预设上限就不再接受 。
关于 TFO 的优化,可以总结出三点内容: