学习资源 小林coding 2022.4.7
TCP实现可靠传输的方式之一 通过序列号与确认应答
TCP 当发送端的数据到达接受主机时 接收端主机回返回一个确认应答消息
表示已收到消息
TCP针对数据包丢失的情况 会使用重传机制
解决
常见的重传机制
在发送数据时 设定一个定时器 超过时间没有收到对方的ACK确认 就会重发该数据
超时时间应该设置为多少
RTT(往返时间)
RTT是数据从网络一段传送到另一端所需的时间 也就是包的往返时间
RTO的值是一个动态变化的值
Linux如何计算RTO
需要TCP通过采样RTT的时间 然后进行加权平均 算出平滑RTT值 值是不断变化的
采样RTT的波动范围
超时重传策略:超时间隔加倍
每当遇到一次超时重传的时候 都会下一次超时时间间隔设为先前值的两倍
两次超时 就说明网络环境差 不适宜频繁发送
TCP还有快速重传机制
不以时间为驱动 以数据驱动重传
重传之前的一个 还是重传所有的问题
SACK Selective Acknowledgement 选择性确认
需要在TCP头部选项中加一个SACK
字段 将缓存的地图发送给发送方
只重传丢失的数据
要支持SACK 在linux下
net.ipv4.tcp_sack
linux2.4之后默认打开
使用SACK告诉发送方 有哪些数据被重复接受了
D-SACK好处
TCP-> 每发送一个数据 都要进行一次确认应答
数据包往返时间越长 通信的效率就越低
TCP引入窗口
这个概念 往返时间较长情况下 不会降低网络通信的效率
窗口大小 : 无需等待确认应答 而可以继续发送数据的最大值
窗口实现:操作系统开辟的缓存空间 发送方主机在等到确认应答的消息之前 在缓冲区保存已发送的数据 如果按期收到确认应答 数据可以从缓存区中清除
如果窗口大小为3个TCP段 那么发送方可以连续发送
3个TCP段 中途如果有ACK丢失
可以通过下一个确认应答进行确认
如果确认应答报文丢失 可以根据ACK确认 只要发送方收到了ACK700确认应答 意味着700之前的所有数据 都收到了
该模式为累计确认
或者累计应答
窗口大小由哪一方确定?
TCP头中有一个字段叫做Window 也就是窗口大小
这个字段是接收端告诉发送端自己还有多少缓冲区可以接受数据
发送端根据接收端的处理能力来发送数据 不会导致接收端处理不过来
通常窗口的大小是由接收方的窗口大小来决定的
发送方发送的数据大小不能超过接收方的窗口大小 否则接收方就无法正常接收到数据
发送方的滑动窗口
数据全部发送和出去之后 不收到ACK确认之前无法继续发送数据
如果确认应答之后 发送窗口的大小没有变化 滑动窗口往右面移动五个字节 有5果然字节的数据被响应应答 又变成可用窗口 后续可以继续发送
程序如何表示发送方的四个部分?
TCP滑动窗口方案中使用三个指针 跟踪四个传输类别中每一个类别的字节 其中两个是绝对指针 一个是相对指针
SND.WND
:表示发送窗口的大小(大小是由接收方指定的);SND.UNA
:是一个绝对指针,它指向的是已发送但未收到确认的第一个字节的序列号,也就是 #2 的第一个字节。SND.NXT
:也是一个绝对指针,它指向未发送但可发送范围的第一个字节的序列号,也就是 #3 的第一个字节。SND.UNA
指针加上 SND.WND
大小的偏移量,就可以指向 #4 的第一个字节了。可用窗口大小 = SND.WND - (SND.NXT-SND.UNA)
接收方的滑动窗口
其中三个接收部分,使用两个指针进行划分:
RCV.WND
:表示接收窗口的大小,它会通告给发送方。RCV.NXT
:是一个指针,它指向期望从发送方发送来的下一个数据字节的序列号,也就是 #3 的第一个字节。RCV.NXT
指针加上 RCV.WND
大小的偏移量,就可以指向 #4 的第一个字节了。接收窗口和发送窗口的大小是相等的吗?
不是完全相等 约等于
接收方读取很快 -> 接收窗口可以很快的空缺出来 -> 新的滑动窗口大小可以通过TCP中Windows字段告诉发送方 传输过程存在时延
发送方不能无脑发数据 如果对方处理不过来 会导致触发重传 网络流量无端的浪费
TCP提供一种机制可以让发送方根据接收方的实际接受能力控制发送的数据量 这就是所谓的流量控制
发送窗口和接受窗口中存放的字节数 都是放在操作系统内存的内存缓冲区中的
操作系统的缓冲区 会被操作系统调整
应用进程没办法及时读取缓冲区内容的时候 也会对我们的缓冲区产生影响
当服务器资源非常紧张的时候 操作系统可能会直接减少缓冲区大小 应用程序无法读取缓存数据 会丢包
为防止这种状况 TCP不允许同时减少缓存又收缩窗口的 采用先收缩窗口 过段时间再减少缓存 避免了丢包情况
如果窗口大小为0时 组织发送方给接收方传递数据 知道窗口变为非0为止 这就是窗口关闭
窗口关闭潜在风险
接收方向发送方通告窗口大小 通过ACK报文通告
接收方处理完数据后 向发送方通告一个窗口非0的ACK报文 如果中国通告窗口的ACK报文在网络中丢失
会让双方都等待通知 形成死锁
TCP如何解决窗口关闭潜在死锁
TCP为每个连接设置一个持续定时器 只要TCP连接一方收到对方的零窗口通知
就会启动持续计时器
如果计时器超时 就会发送窗口探测报文 对方在确认探测报文时 给出自己现在的接受窗口大小
窗口探测的次数一般为3此 每次大约30-60s 如果3次过后接受窗口还是0
TCP实现会发RST中断连接
糊涂窗口综合征
接收方忙 -> 来不及取走接受窗口里面的数据 会导致发送方的发送窗口越来越小
如果接收方腾出几个字节并且告诉发送方现在有几个字节的窗口
发送方会全部发送
TCP+IP头有40字节 开销太大 不经济
怎么让接收方不通告小窗口
接收方策略:
当窗口大小小于min(MSS,缓存空间/2) 小于MSS与1/2缓存空间的最小值
直接向发送方发送窗口为0
等空间大了再通知发送方
如何让发送方避免发送小数据?
发送方通常策略:
Nagle算法 延时处理
可以在 Socket 设置 TCP_NODELAY
选项来关闭这个算法(关闭 Nagle 算法没有全局参数,需要根据每个应用自己的特点来关闭)
setsockopt(sock_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&value, sizeof(int));
拥塞控制与流量控制
流量控制 -> 避免发送方的数据填满接收方的缓存
但是并不知道网络中发生了说明
计算机网络处在一个共享的环境 有可能会因为其他主机之间的通信使得网络拥堵
当网络出现拥堵时 如果继续发送大量数据包 可能会导致数据包时延 丢失等 这时TCP就会重传数据 一重传就会导致网络负担更重 会导致恶性循环
拥塞控制 -> 避免发送方的数据填满整个网络
发送方调节发送数据的量 -> 定义了一个拥塞窗口的概念
什么是拥塞窗口 和发送窗口有什么关系?
拥塞窗口是发送方维护的一个状态变量
(cwnd)
会根据网络的拥塞程度动态变化
发送窗口 = 拥塞窗口和接受窗口的最小值
变化规则
如何知道是否拥塞?
如果发生超时重传 就会认为网络出现了拥塞
拥塞控制有哪些控制算法?
发送方每收到一个ACK 拥塞窗口大小+1
慢启动涨到什么时候是个头呢?
慢启动门限 : ssthresh 状态变量
ssthresh大小一般是65535字节
每收到一个ACK cwnd增加1/cwnd
线性增长
当触发了重传机制 也就进入了拥塞发生算法
超时重传 拥塞发生
这个时候,ssthresh 和 cwnd 的值会发生变化:
ssthresh
设为 cwnd/2
,cwnd
重置为 1
发生快速重传的拥塞发生算法
cwnd = cwnd/2
,也就是设置为原来的一半;ssthresh = cwnd
;正如前面所说,进入快速恢复之前,cwnd
和 ssthresh
已被更新了:
cwnd = cwnd/2
,也就是设置为原来的一半;ssthresh = cwnd
;然后,进入快速恢复算法如下:
cwnd = ssthresh + 3
( 3 的意思是确认有 3 个数据包被收到了);