文章插图
- 数据包 1 期望的下一个数据包 Seq 是 1 , 但是数据包 2 发送的 Seq 却是 10945 , 说明收到的是乱序数据包 , 于是回了数据包 3 , 还是同样的 Seq = 1 , Ack = 1 , 这表明是重复的 ACK;
- 数据包 4 和 6 依然是乱序的数据包 , 于是依然回了重复的 ACK;
- 当对方收到三次重复的 ACK 后 , 于是就快速重传了 Seq = 1 、Len = 1368 的数据包 8;
- 当收到重传的数据包后 , 发现 Seq = 1 是期望的数据包 , 于是就发送了个确认收到快速重传的 ACK
注意:快速重传和重复 ACK 标记信息是 Wireshark 的功能 , 非数据包本身的信息 。
以上案例在 TCP 三次握手时协商开启了选择性确认 SACK , 因此一旦数据包丢失并收到重复 ACK , 即使在丢失数据包之后还成功接收了其他数据包 , 也只需要重传丢失的数据包 。如果不启用 SACK , 就必须重传丢失包之后的每个数据包 。
如果要支持 SACK , 必须双方都要支持 。在 Linux 下 , 可以通过 net.ipv4.tcp_sack 参数打开这个功能(Linux 2.4 后默认打开) 。
#TCP 流量控制
TCP 为了防止发送方无脑的发送数据 , 导致接收方缓冲区被填满 , 所以就有了滑动窗口的机制 , 它可利用接收方的接收窗口来控制发送方要发送的数据量 , 也就是流量控制 。
接收窗口是由接收方指定的值 , 存储在 TCP 头部中 , 它可以告诉发送方自己的 TCP 缓冲空间区大小 , 这个缓冲区是给应用程序读取数据的空间:
- 如果应用程序读取了缓冲区的数据 , 那么缓冲空间区就会把被读取的数据移除
- 如果应用程序没有读取数据 , 则数据会一直滞留在缓冲区 。
接收窗口的大小 , 是在 TCP 三次握手中协商好的 , 后续数据传输时 , 接收方发送确认应答 ACK 报文时 , 会携带当前的接收窗口的大小 , 以此来告知发送方 。
假设接收方接收到数据后 , 应用层能很快的从缓冲区里读取数据 , 那么窗口大小会一直保持不变 , 过程如下:
文章插图
但是现实中服务器会出现繁忙的情况 , 当应用程序读取速度慢 , 那么缓存空间会慢慢被占满 , 于是为了保证发送方发送的数据不会超过缓冲区大小 , 服务器则会调整窗口大小的值 , 接着通过 ACK 报文通知给对方 , 告知现在的接收窗口大小 , 从而控制发送方发送的数据大小 。
文章插图
#零窗口通知与窗口探测
假设接收方处理数据的速度跟不上接收数据的速度 , 缓存就会被占满 , 从而导致接收窗口为 0 , 当发送方接收到零窗口通知时 , 就会停止发送数据 。
如下图 , 可以看到接收方的窗口大小在不断的收缩至 0:
文章插图
接着 , 发送方会定时发送窗口大小探测报文 , 以便及时知道接收方窗口大小的变化 。
以下图 Wireshark 分析图作为例子说明:
文章插图
- 发送方发送了数据包 1 给接收方 , 接收方收到后 , 由于缓冲区被占满 , 回了个零窗口通知;
推荐阅读
- 一文说清“保障房” 保障房是什么?
- 坏男孩教会我的17件事 那些坏男人教我的事
- 一文了解高岭土加工技术及特点 高岭土成分
- 教你一文看懂股票涨幅榜 股票涨幅怎么算
- |一文读懂办公室工作忌讳!
- 一文带你了解什么是Quora广告
- 私域实战|5000字精华,讲透私域社群运营!
- 一文读懂IP地址和MAC地址有什么区别和联系 mac地址是什么
- 如何教会孩子认识钟表 认识钟表课件
- 如何带领“钢铁直男”,教会他成为你心中的暖男!