Overview
本文的主角是TCP SACK
机制,首先讲清楚为什么我们需要SACK
机制,其次通过wireshark
抓包展示真实世界中的SACK
,再者引申经常与SACK
一起出现的概念,讲清楚SACK
机制并不是孤立的概念。要真正解决现实中的问题,是需要与其他TCP
协议内容一起作用滴!
为什么需要SACK
在讨论为什么需要SACK
之前,我们应该先了解TCP
接收数据的一个机制:它只能确认连续的数据,它也只能消费前面的连续数据。
TCP is never able to acknowledge data it has received correctly but that is not contiguous A receiving TCP prevents applications from consuming data beyond a hole because of the byte stream abstraction it provides.
举个例子:
比如sender
发送了10
个包:#1,#2,#3,#4,#5,#6,#7,#8,#9,#10
,但是#5
在中途丢失了,对于sender
和receiver
来说,就好比数据中间产生了一个"洞"hole
。
tcp
接收方receiver
的ack
只能是接收了#4
,表示该接收#5
了。再一次重申,因为tcp
协议的ack
机制只能接收连续的数据,如果#5
丢失了,即便#6
,#7
,#8
,#9
,#10
全部到达receiver
,receiver
也无法跨过#5
告诉sender
我只是丢了#5
,后面的包到没到达sender
是不知道的。
此时sender
该怎么做?sender
不知道具体情况,它只能一股脑将#5,#6,#7,#8,#9,#10
全都重新发送一次。
像上面的情形:在一连串数据的发送过程中,中间的数据包很有可能丢失,如果每次都是重新发送一遍,首先效率低,其次浪费网络链路带宽。
如果sender
能够得知自己发送的数据中间产生了一个洞(中间数据包丢了),那就太好了,因为如此一来,sender
就可以对症下药的发送真正缺失的数据。
sender
怎么才能知道这一信息呢?只能靠receiver
的配合,receiver
根据自己的接收情况告诉sender
发送过程中产生的"洞"
If a TCP sender were able to learn of the existence of holes at the receiver, it could better select which particular TCP segments to retransmit when segments are lost or otherwise missing at the receiver.
没错,所谓SACK
就是Selective Acknowledgement
。selective
指的就是上述select lost segments to retransmit
,怎一个妙字了得。
关于SACK
的概念,我们就介绍到这里,下面根据真实案例来学习SACK
的细节。
真实世界中的SACK
经过抓包,你会发现SACK
经常会和两个好兄弟一起出现。
它们分别是Wireshark Expert Infos
中的「TCP previous segment not captured
」和「TCP Dup ACK
」。
下面直接来看数据包的抓取吧~
发送者sender
的端口为30000
,接收者receiver
的端口为1479
。
再给出具体分析之前,大家可以先自己看看,注意表头的含义。
那么,现在我们开始分析。
#10415号包
receiver
说:嘿,前面的数据我都接收到了,接下来麻烦你从seq=9163441
开始发送的
ps
:TCP Len
的长度为0
,表示这个包只是一个ack
包,没有携带任何应用数据。
#10416号包
sender
说:好的,俺这就从seq=9163441
开始发送,并且我带了1320 bytes
的数据,我下一个数据包从seq=9164761
起开始发送哈~
ps
:到这里一切都没有问题。
#10417号包
开始发生意外了,何以见得?wireshark
提示「TCP previous segment not captured
」了。
为什么呢?因为数据不连续了。
10416
号包说下一个从seq=9164761
开始发送,结果10417
号包的seq=9175321
,中间还有一堆数据没有发送呢!
wireshark
意识到中间可能有点问题,就即使提醒我们了。
这不就是一开始举例子的时候说的那样,连续发送包,然而中间丢失了数据,但SACK
能够记录这一事件。接下来就看10418
到底是如何处理的!
#10418号包
receiver
:我宣布两件事,1)我的Ack=9164761
,表示#10416
号包的数据我接收到了。2)我的TCP Option
中有SACK
信息,9175321-9176641
表示#10417
号的内容我收到了。sender
你知道了吗?
#10419号包
sender
:接着#10417
的内容,俺继续发送(我还没意识到我需要重传前面的一些数据)
#10420号包
receiver
:咳咳,我再次重申,我的Ack=9164761
,表示#10416
号包的数据我接收到了,这已经不是我第一次Ack
这个seq
(TCP Dup ACK
),请sender
注意📢。其次,你发的其余数据包我都接收到了,请看我的TCP Option
中SACK
的部分。
你有没有恍然大悟,为了让事情变得更清晰,让我们再次从全局看一下这次的抓包情况:
首先来看红色和蓝色箭头部分:receiver
一直声明自己的ack=9164761
,表示自己按照顺序接收到了此ack
之前的所有数据,但是它一再声明(TCP Dup Ack
)我只连续收到这里,后面的数据包可能确实一段数据或者多段数据,具体请看我的SACK
中的信息。
然后就是receiver
中的SACK
信息,绿色方框部分表示receiver
跨过可能丢失的数据段,左侧范围一直从seq=9175321
开始,而这正好是#10417
包发送的数据内容。后面随着sender
不断发送数据,receiver
的SACK
右侧范围不断扩大,这一点从黄色方框谁可以看出来的。
引申问题
所有的sender和receiver都可以使用SACK机制吗?
不是的,是否可以使用sack
机制,在三次握手的过程中确定。
只要双方在TCP Option
中,都明确的开启SACK permitted
才表示接下来的数据传输过程中,可以使用SACK
机制加快数据传输的恢复。
为什么要有TCP Dup Ack,它是干啥的?
一句话回答:receiver
通过SACK
告知sender
可能缺失的数据,Dup Ack
告诉sender
重新发送的实机。
Dup Ack
顾名思义,就是receiver
重复声明,俺已经接收过某某包了。你仔细一想,其实这是SACK
出现必不可少的辅助。
对于网络链路来说,除了丢包,还有可能出现乱序的情况。
比如发送#1,2,3,4,5
,本来是顺序发送的,结果经过不对称的网络链路,发生了乱序,receive
收到的顺序为13245
。
receiver
接收到#1
,然后发送ack=#1
。
receiver
接收到#3
,表示发送ack=#1
,sack=#3
。
sender
接收到以后要立马发送#2
吗?不需要的,因为receiver
紧接着就会收到#2
,从此中间数据缺失产生的洞就补上了。因为这种场景仅仅是乱序,是不需要重传的?
那如何才能区分乱序和丢包呢?其实这是很难的,TCP
协议能做的,就是尽量区分一下,也就是所谓的妥协。
TCP
如何认为这是需要重传的呢?
一定时间内,接连收到三次及以上同样ack
的Dup Ack
,wireshark
就认为这包啊,sender
需要重传了,否则sender
不必过多关注这一信息。
Summary
关于SACK
,我表达清楚了吗?