目录
0 引言
前面一篇文章对TCP连接建立和关闭的知识进行了归纳,这篇将对TCP连接传输数据过程中的相关知识点进行归纳
1 TCP连接为什么是可靠的
在进入正题前,首先来一个问题:TCP连接为什么是可靠的?对于这个问题,主要由以下几点原因
- 校验和:TCP首部有一个校验和字段,通过该字段可以对传输的数据进行校验,来判断数据在传输的过程中是否出错
- 请求应答机制:我们的TCP报文的传输是基于请求应答机制的,也就是一方发送的报文一定会接受对方对于该报文的确认报文
- 重传机制:当我们的报文在网络中被阻塞或者丢失时会触发发送方的超时重传机制
- 滑动窗口和流量控制:基于滑动窗口的流量控制保证了接受方一致有足够的空间接受发送方的数据
- 拥塞控制:拥塞控制算法可以根据网络状况动态控制发送发发送窗口的大小
下面就对以上的部分原因进行详细介绍
2 重传机制
2.1 超时重传机制
当发送方放松一个报文会开启一个定时器,当定时器时间到了还没有受到报文的ACK报文时就会重传该报文,这个重传定时器的时间为RTO,一般略大于一个RTT时延
超时重传一般发生在发送报文或者接受方确认报文丢失的情况下
2.2 快速重传
快速重传是说当我们发送方反复接收到同一个ACK报文达到一定次数后就会重发原报文;过程示例如下:
- 发送方按序发送了五个序列号的报文seq1~5
- 其中seq2丢失了
- 服务端对于seq1报文回复ACK2报文
- 服务端对于后面的seq3~5报文同样回复ACK2报文
- 客户端发现接受到了3个重复的ACK报文,就知道seq2报文丢失了,开始重发seq2报文
2.3 SACK机制
超时重传机制和快速重传机制都存在一个问题,就是是重传一个报文还是重传后面所有的报文,引入sack即可帮我们解决这个问题;所谓的sack机制就是接收方在给发送方发送ACK报文时,会带上一个一个叫做sack的东西来告诉发送方自己的缓冲区接受数据的情况。就拿上面快速重传的例子来说,服务端对于seq3~5的ACK报文中会加上一个sack
- seq3的ack2报文加上sack3
- seq4的ack2报文加上sack3-4
- seq5的ack2报文加上sack3-5
这样seq5的ack2报文到达客户端时会触发快重传,同时客户端通过sack得知是只有seq2丢失了,所以只会重发seq2报文
2.4 D-SACK
有时因为网络原因导致发送方重发了一些数据,最后接收方会接受到新旧两份相同的数据,当接收方发现自己接受到重复的数据时就会通过sack告诉发送方自己重复接受了哪些数据
3 滑动窗口
3.1 为什么需要滑动窗口
首先来看为什么要引入滑动窗口,我们的tcp是请求应答的方式发送和接受数据,这样你一句我一句的方式显然效率低下,所以就在想能否发送方发送一段数据,然后发送方对这段数据进行确认,这也就引入了我们的滑动窗口,滑动窗口实际上就是内存中的一块缓冲区
3.2 滑动窗口大小如何确定
发送方可以通过接收方报文中的窗口大小字段来设置自己的窗口大小
3.3 发送方窗口
发送给方数据主要分为四个部分:
- 窗口外已发送并确认接受了的数据
- 窗口内已发送但还没确认接受的数据
- 窗口内还能够发送的数据
- 窗口外不能发送的数据
3.4 接受方窗口
接收方数据主要分为以下三个部分
- 窗口外已接收并成功确认的数据
- 窗口内未接受但能够接受的数据
- 窗口外未接受但不能够接受的数据
4 流量控制
TCP的流量控制是基于滑动窗口来实现的,主要是为了保证发送方不至于发送太多的数据导致接收方不能接收处理。
主要过程:
发送方
-
发送方在发送可用窗口内的数据后会挪动可用窗口的边界指针
-
发送方在接收到接收方在接受到发送数据的确认信息后会挪动窗口的边界指针
-
发送方在接收到接受方的窗口大小后会改变自己的窗口大小
接收方
-
接收方在接收到数据后,会发送接受数据的确认报文
-
接收方接收的数据在缓冲区中可能由于服务端的处理能力有限或者服务端繁忙而导致接收的数据占用缓冲区,此时接收方会缩小窗口的大小,并连同确认信息一起将此时的窗口大小发送给发送方
窗口关闭
-
首先将窗口调整为0的一方会开启一个定时器,在定时器到点后会发送窗口探测报文,对方接收到这个窗口探测报文后就会给出自己现在的窗口大小
糊涂综合征:
-
概念
接收方有多少子节点的窗口就会告诉发送方,然后发送方会义无反顾的发送这几个字节的数据,这就叫做糊涂窗口综合症。我们直到TCP+IP的头部就有40个字节,若为了发送那么几个字节的数据而搭上这么大的开销是不值得的。
-
产生原因
-
接收方可以发送一个小的窗口
-
发送方可以发送小数据
-
-
如何避免
-
让接收方不通知小窗口给发送方
-
让发送方不发送小数据
-
5 拥塞控制
为什么需要拥塞控制
-
流量控制是为了避免发送方的数据填满接收方数据的缓存
-
流量控制并不能告诉我们网络上发生了什么,即网络上的数据可能发生阻塞,延迟等
-
因此需要拥塞控制,来帮助我们避免发送方的数据填满网络
拥塞窗口
-
拥塞窗口是发送方维持的一个动态变量,会根据网络的拥塞程度动态变化
-
引入拥塞窗口cwnd后,接收方的接收窗口大小就是swnd=min(cwnd, rwnd)即拥塞窗口和接受方窗口的最小值
-
拥塞窗口的变化规则:网路状况越好,窗口越大,否则越小
拥塞控制算法
-
慢启动
当发送方每接收到一个ACK,拥塞窗口cwnd的大小就会加1
慢启动的发包个数是指数型增长的
-
拥塞避免
-
那么慢启动涨到什么时候是一个头呢?这是后就引入了拥塞避免算法,当cwnd小于慢启动门限ssthresh时就会启动慢启动算法,当cwnd大于等于慢启动门限时就会使用拥塞避免算法
-
规则:没接收到一个ACK,cwnd就增加1/cwnd。发包个数也就成了线性增长
-
-
拥塞发生
当网发生拥塞时,也就会发生数据包的重传,重传机制有两种:
-
超时重传
ssthresh变为当前cwnd的一半,cwnd直接变为1然后又开启新一轮的慢启动算法
-
快速重传
cwnd变为当前cwnd的一半,ssthresh变为当前的cwnd(更新后的cwnd,也就是旧cwnd的一半)
然后进入快速恢复算法
-
-
快速恢复
-
cwnd = ssthresh + 3 (3的意思表示三个数据包接收到了)
-
重传丢失的数据包
-
如果再次受到重复的ACK,则cwnd+1
-
如果受到新的ACK,那么就说明可以恢复到原来状态了,也就是再次进入拥塞避免算法
-