TCP
![](https://img.haomeiwen.com/i5899140/0c80acce535c5911.png)
三次握手都干了什么
- 双方都交换了自己的序列号
- 双方都确认了各自的消息都可以成功被处理
- 初始化各自的sockets,窗口大小和初始系列号
三次握手的设计目的
-
防止历史的重复连接初始化造成混乱,也就算说防止简历错误的连接
如果只有2次连接
因为网络的不稳定性,
client
可能会重复发送sync
,接收方在收到后 只能默默的创建,但是有可能这个连接已经是过期的了。所以需要有三次
在接收方server收到之后,会对
seq
进行确认发送ack,这时候发送方会判断这次的ack是否有效,如果有效则确认,如果无效是会直接发送rst
中止这一次连接那么四次可不可以
可以,但是没必要。不要增加无谓的负担。
三次握手把控制权交给了发起方,因为他们有足够的上下文来确定当前要建立的连接是否有效。
-
初始化序列号
在一个不可靠的网络上,我们的tcp报文可能出现以下的情况
- 数据包重复发送
- 数据包中间丢失
- 数据包无序到达
因为,tcp协议中加入了序列号的字段,通过序列号,我们可以
- 对重复的数据包进行去重
- 对丢失的数据包(未被ack)进行补发
- 对乱序的数据包进行排序
拥塞控制
慢启动
最开始的时候,没收到一个ack 则 窗口+1
拥塞避免
当达到一定阈值的时候,没经过一个rtt则+1,如果出现丢包,窗口减少为1,阈值减少为当前的一半。
快重传
对于未被确认的,发送连续确认ack,触发快速重传,不再等待超时。
快恢复
丢包的时候,窗口减少为阈值
流量控制
目的是为了点对点之间不要过分暴力
sender: 需要维持一个滑动窗口,代表的是接收端的一个消费能力
- 已发送
- 未发送,待发送
- 不可发送
receiver:需要维持一个滑动窗口
- 已确认
- 未确认
如果接收端最大能力是10,但是因为已经确认了10个了,那个窗口大小就会变为0。
四次挥手
![](https://img.haomeiwen.com/i5899140/58a83c821000e5ed.png)
TIME_WAIT设计的目的
- 防止延迟的数据段被新建立的TCP链接接收到
- 保证TCP连接的远程被正确关闭
阻止延迟数据段
![](https://img.haomeiwen.com/i5899140/f8209ac813d0f1d2.png)
对于上图,如果在close之后,又启了一个tcp链接。这个时候之前迷路的 seq301 到了,就会被错误的接收。
保证TCP连接的远程被正确关闭
在server没有收到确认的情况下,会再次发送fin,这个时候需要重新ack。或者rst。
UDP
这次,当我想到UDP的时候,我想到的是:如果他不提供任何可靠性保障,那么我为什么还需要它呢。
查找资料发现,它再ip数据报的上面封装了 源端口、目标端口、长度和校验和。其中源端口和校验和也是可选的。
所以它对比ip来说,它仅仅是新增了端口的信息。提供了一个应用程序层面的多路复用。
虽然这样,但是后续http3确实基于udp实现的。
HTTP2
http2.0 使用的是二进制编码传输,而不是之前的文本编码传输。
解决了什么问题
-
连接无法复用
- 如果复用,会引来队首阻塞问题
- 如果不复用,会带来多个tcp创建的问题
-
Head of line blocking
- fifo(慢请求的话就特别危险)
-
减少了网络拥塞
根据tcp的慢开始和拥塞控制算法,在其传输大块数据时效率才可以达到最高。HTTP2.0更好的压榨了tcp性能。
提供了什么特性
- 二进制编码分帧
- 连接复用
- header压缩
- 哈夫曼
- 服务端推送(猜你所想)
带来了什么问题
- 消除了http队首阻塞,但是tcp层的队首阻塞仍然存在
- 一旦丢包,也会触发tcp的拥塞控制
HTTP3
http2虽然做了很多优化,(二进制消息帧,头压缩,消息多路复用,服务器推送,流量控制等)。
但是由于其是基于tcp实现的,tcp上的问题还是没有解决(队首阻塞,重初始化)
此外tls的队首阻塞也是一个性能瓶颈
![](https://img.haomeiwen.com/i5899140/069eeed275b05e26.png)
提供了哪些特性
-
可插拔
因为QUIC是在应用层实现的,所以更加灵活。不需要内核支持,便可以实现,并且便于升级。
-
序列号(packet number)递增
不同于tcp的序列号,quic的序列号是严格递增的,这样的话就可以防止RTT计算出现误差。
同时也提供了offset来保证消息的有序性。
-
更多的ACK块
通过ack block,可以告诉发送方更多的信息,以便选择性回传
-
新增ACK delay
为了是RTT更加精准
-
流量控制(和HTTP2类似)
-
没有队首阻塞的多路复用
-
tcp的改进
之前http2的基于tcp,仍然可能存在tcp层的队首阻塞,当一个包丢失后,后续的包都不能被应用层读取
image.png
-
-
tls的改进
http2强制使用tls,record是tls协议的最小单位。如果目前由于丢包,导致record不完整,这个时候是没有办法进行一致性校验的。
image.png
所以QUIC使用packet传输,tls加密也是基于packet,不会跨packet,也就不会存在则塞的现象。
-
ORTT创建连接
Image
不需要繁琐的tcp和tls握手,quic最快可以做到直接发送数据。
具体0RTT实现后面细讲。
-
前向纠错
-
连接迁移
时代变了,那个基于网线的时代一去不复返了
连接标识:
TCP: SIP+SPort+DIP+DPort+协议号
QUIC: 唯一标志ID
Image
这里即使我们切换网络ip变了,但是id是可以不变的。所以也就不需要重连了。
解决了哪些问题
- tcp & tls 队首阻塞
- 不灵活的协议升级
- 过于笨重的实例握手
- rtt计算的不精确
- 对于频繁变动的网络支持不友好,之前TCP设计的时候还是考虑的网线场景。
再谈0RTT
0RTT并不是银弹,他需要client保存服务端的config(这个config是基于证书的)。
如果是首次交互
-
C发起请求
-
S首次回应C
S将config封装成数据包给C,其中包含{p,g,k_pub}
-
C发送加密的数据
C收到config,生成k_c_pir,根据
g,p
计算出公钥,和对称秘钥然后利用对称秘钥加密数据进行传输
-
S得到对称C的公钥,为了向前安全性,计算出一对新的公私钥进行通信。
-
C收到S的包,解析出新的公钥,计算出新的对称秘钥,后续保持通信。
如果非首次交互
因为C中已经保存了config,所以可以直接从3开始,这次也是同理会生成一个新的对称秘钥。
下图是交互的一个过程:
![](https://img.haomeiwen.com/i5899140/e98c89316e636d77.png)
带来了哪些问题
- 0-RTT 连接恢复是不提供前向安全的forwardsecrecy,上面的过程大家可以看到,他复用的是之前的config。但是如果恢复的请求被中间人攻击,进行多次发送,那如果在没有幂等的情况下就会出现很多问题。
![](https://img.haomeiwen.com/i5899140/4a69e09e68c8f20d.jpeg)
- 安全问题,也就是反射攻击,即伪造原地址。这个指发送数据包时的原地址是伪造的,不是真正的地址,会引起放大攻击。原因是 QUIC 握手过程是不对称的,特别是第一次请求时,客户端只需要发送几个字节的信息到服务器,而服务器则需要把证书等很多东西返还给客户端,这个不对称的机会造成了放大。草案 27 定义了两个规则和机制来限制反射攻击:客户端发送Initial包,即第一个数据包时,其长度必须在 1200 bytes以上,不足部分用 Padding 帧填充,同时,当服务端不确定客户端可靠性时,可以发送 Retry 包要求客户端再次提供验证信息。
- 兼容问题,UDP的443端口可能会被封杀,一些激进的安全策略对外只暴露80端口
- UDP包过多,可能导致误判为攻击。
- 针对UDP的优化太少了,无论是防火墙还是路由器。
感悟
花了多半天时间大概了解了下http的发展历程,脑海里飘过这样一句话:一个人可以跑的很快,一群人可以跑的很远。
这么多年我们的很多应用都是基于tcp的,tcp给我们提供了很多特性,就行我们目前的中台,提供各种丰富的功能,但是因为是中台,他变的笨重,因为兼容,它变的不敏捷。于是有了QUIC,quic说你提供的这些功能我自己也可以实现,并且结合自己的场景实现的更好,于是又了QUIC。有了更快的网络。就行我们说的go协程,不依赖于CPU线程调度一样。
常见名词
RTT
往返时延,round-trip time.
即在网络中,从发送方发送数据开始到收到数据的确认直接的时间。
网友评论