TCP(滑动窗口/拥塞窗口补充)

目录

[一 滑动窗口](#一 滑动窗口)

[1. Zero Window](#1. Zero Window)

[2. Silly Window Syndrome](#2. Silly Window Syndrome)

[二 SACK/DSACK](#二 SACK/DSACK)

[1. SACK](#1. SACK)

[2. DRACK](#2. DRACK)

[三 拥塞控制](#三 拥塞控制)

[1. Congestion Avoidance](#1. Congestion Avoidance)

[2. TCP New Reno](#2. TCP New Reno)


一 滑动窗口

1. Zero Window

对端通告窗口大小为 0 ,接收端就不能在发送数据。

发送方会尝试探测对端窗口大小,即发送 ZWP (Zero Window Probe)报文,对端回复窗口大小,如果发多次 ZWP 得不到响应或者一直通告窗口为 0 则会关闭连接或者触发 RST 连接重置。

2. Silly Window Syndrome

对端通告窗口大小为 1,2,3.... 这种很小的窗口,发送端就只能发送很小的数据,但基于网络协议栈进行封包,TCP/IP最少报头都是 20 + 20,不携带选项,在携带这么少的数据,也就是 MSS,开销比较大。

  1. 接收端引起

可以采用 David D Clark's 方案,如果接收的数据之后的窗口小于某个阈值,则直接通告窗口大小为 0 ,后续接收端处理数据腾出的空间大于 MSS,或者 接收缓冲区一半已经空闲,或者达到某个阈值,才会重新通告发送端窗口值。

  1. 发送端引起

可以采用 Nagle's algorithm 方案,如果对端已经收到所有的报文或者刚连接什么也没发,也就是发送的报文全部都 ACK 了,则无论多大的包都直接发送,否则如果还有的报文没有 ACK,后续写的数据会积攒起来,如果积攒超过 MSS 则会打包发过去,或者前面发的所有包都 ACK 了也会直接发出去。

二 SACK/DSACK

1. SACK

  1. 触发超时重传,中间丢了多个包的,具体是重发一个还是都发?所以引入了 SACK。

SACK:需要双方握手的时候携带选项进行协商。

  • SACK 会记录已收到的非连续的报文序号,这样对端就能通过 ACK 和 SACK 得到哪些包丢了,比如:发 1000 2000 3000 4000 5000 6000,2000 3000 丢了,收到 1000,4-5-6000,ACK 3 次 2001,触发超时重传。
  • SACK:4 - 6000 收到了
  • ACK: 2001 之前的收到了
  • 发送端根据 ACK 和 SACK 把 2000-3000 重发。

缺点:

上述的 SACK 的数据包只是辅助重传哪些包,但不是真正的 ACK,可能因为某些原因,比如内存不足,接收缓冲区严重不足,回应 RACK 的时候把 RACK 的这些包丢弃了,因为不是正真确定的 ACK 的包。

2. DRACK

  • RACK 的一个子集,如果 RACK 的第一个字段被 ACK 覆盖 或者 第一个字段被 RACK 的第二个字段覆盖,那这个 RACK 就是 DRACK ,作用通知接收端哪些数据重发了

比如:

  1. ACK丢包:发送 1,2,3。 1,2 的 ACK 丢了,触发超时重传,重传 1,但回复的是 ACK 3(3之前的收到了),DRACK 1(重复收到1) 。

  2. 网络延迟:发送 1,2,3,4,5。 2 因为延迟,导致3,4,5先收到触发超时重传,重传 2,后续回复 ACK 5,DRACK 为空。延时的到了 ACK 5,DRACK 2。

所以 DRACK 可以有效的表示:

  • 发出的包丢了还是 ACK 的包丢了,ACK 丢了见上文,发出的包丢了根据后续报文回复的 ACK 即可。
  • 因为自己 超时重传 时间短导致重复发送报文,比如发 1,ACK 还没到或者丢了就 重传了,接收端就会再次 ACK 1,SACK 1。
  • 包乱序问题,先发的后收到,见上文。

SACK 和 DRACK 的值:SACK 的值会大于 ACK,RSACK 的值会小于 ACK。

三 拥塞控制

1. Congestion Avoidance

当拥塞窗口超过 ssthresh(慢启动阈值),则会进入 Congestion Avoidance(拥塞避免),后续拥塞窗口大小呈线性增加。

  1. 如果触发超时重传

最坏的情况:

  • ssthresh = 当前窗口大小 / 2
  • ssthresh = 1

随后继续进行慢启动。

  1. 如果触发快速重传

  2. TCP Tahoe 的实现和超时重传一样。

  3. TCP Reno 则会:

  • 窗口大小 = 当前窗口大小 / 2
  • ssthresh = 窗口大小

进入快速恢复阶段:

  1. 因为是快速重传,收到连续的 3 个 ACK,表示有 3 个包收到了,窗口大小 = ssthresh + 3 * MSS。

  2. 重传 ACK 指定的包,如果还是收到 ACK 指定的包则 窗口大小 = 窗口大小 + 1* MSS,如果收到新的 ACK,则 窗口大小 = ssthresh,随后进入拥塞避免阶段。

2. TCP New Reno

  1. 上述快速重传如果中间丢了多个包,只会重传第一个包,后面的则会进行超时重传,执行超时重传的解决逻辑,可以通过前面的 SACK 来动态发送。

  2. 这里引入了 TCP New Reno,在 TCP Reno 的基础上引入了 Partial ACK ,他会记录接收到的包的序列号最大的那一个,每次发送端重传数据包的时候和 Partial ACK 进行比较,小于就代表还有丢的包没重传,直到超过 Partial ACK 表示所有的包已收到。

相关推荐
“αβ”5 小时前
MySQL表的操作
linux·网络·数据库·c++·网络协议·mysql·https
Cher ~9 小时前
【协议】ICMP
网络·网络协议
丁丁丁梦涛10 小时前
EMQX配置 ssl 和 wss
网络·网络协议·ssl·emqx·wss
繁华似锦respect11 小时前
C++ unordered_map 底层实现与详细使用指南
linux·开发语言·c++·网络协议·设计模式·哈希算法·散列表
Wokoo711 小时前
HTTP不同版本核心对比
网络·网络协议·tcp/ip·http·udp·ssl
乾元12 小时前
AI + Jinja2/Ansible:从自然语义到可执行 Playbook 的完整流水线(工程级深度)
运维·网络·人工智能·网络协议·华为·自动化·ansible
ZhengEnCi12 小时前
一次多线程同步问题的排查:从 thread_count 到 thread.join() 的踩坑之旅
python·网络协议·tcp/ip
oxygen-120413 小时前
https nginx步骤
网络协议·http·https
路由侠内网穿透.13 小时前
本地部署问答社区 Apache Anwser 并实现外部访问
服务器·windows·网络协议·apache·远程工作
科技块儿14 小时前
简单易学的IP定位查找教程
网络·网络协议·tcp/ip