哪个 TCP window 更新是最新的?
Which TCP window update is most recent?
我正在编写一个 TCP 实现,完成了所有奇特的慢速和快速重传工作,并且一切正常,所以我认为我已经完成了。但是后来我回顾了我的数据包接收函数(总共400行代码的将近一半),并且意识到我对基本流量控制的理解是不完整的...
假设我们有一个带有“发送方”和“接收方”的 TCP 连接。假设“发送方”没有发送任何东西,而接收方正在停顿然后解除停顿。
由于“发送方”没有发送任何东西,“接收方”看不到 ack_no 增量。所以来自“接收者”的两个 window 更新看起来像:
- ack_no = X, window = 0
- ack_no = X, window = 8K
由于两个数据包具有相同的 ack_no,并且它们可以在传输过程中重新排序,发件人如何知道哪个先到?
如果发件人不知道哪个先来,那么在收到两个数据包后,如何知道是否允许发送?
一种猜测是,也许 window 的上限永远不允许下降?一旦接收方分配了接收缓冲区并通告了它,它就永远无法取消通告了吗?在这种情况下,可以通过以下代码可靠地处理 window 更新(为简单起见,假设没有 window 比例):
// window update (
int ack_delta = pkt_ack_no - c->tx_sn_ack;
c->tx_window = MAX(BE16(PKT.l4.window), c->tx_window - ack_delta);
if (c->tx_window)
Net_Notify(); // wake up transmission
但从接收器的角度来看,这很糟糕:它大大增加了可靠地支持 10K 连接所需的内存。协议肯定比那更聪明?
假设接收缓冲区永远不会缩小,这是有意未记录的,目的是创建一个精英“游戏中的皮肤”俱乐部,以限制 TCP 实现的数量。
original standard 说缩小 window 是“令人沮丧的”,但没有指出它不能可靠地工作:
The mechanisms provided allow a TCP to advertise a large window and
to subsequently advertise a much smaller window without having
accepted that much data. This, so called "shrinking the window," is
strongly discouraged.
更糟糕的是,该标准实际上缺少问题中提出的 MAX 操作,如果确认号没有增加,则只从最近的数据包设置 window:
If SND.UNA < SEG.ACK =< SND.NXT, the send window should be
updated. If (SND.WL1 < SEG.SEQ or (SND.WL1 = SEG.SEQ and
SND.WL2 =< SEG.ACK)), set SND.WND <- SEG.WND, set
SND.WL1 <- SEG.SEQ, and set SND.WL2 <- SEG.ACK.
Note that SND.WND is an offset from SND.UNA, that SND.WL1
records the sequence number of the last segment used to update
SND.WND, and that SND.WL2 records the acknowledgment number of
the last segment used to update SND.WND. The check here
prevents using old segments to update the window.
因此,如果重新排序具有相同 ack 编号的数据包,它将无法增长 window。
底线:实施实际有效的东西,而不是标准中的东西。
我正在编写一个 TCP 实现,完成了所有奇特的慢速和快速重传工作,并且一切正常,所以我认为我已经完成了。但是后来我回顾了我的数据包接收函数(总共400行代码的将近一半),并且意识到我对基本流量控制的理解是不完整的...
假设我们有一个带有“发送方”和“接收方”的 TCP 连接。假设“发送方”没有发送任何东西,而接收方正在停顿然后解除停顿。
由于“发送方”没有发送任何东西,“接收方”看不到 ack_no 增量。所以来自“接收者”的两个 window 更新看起来像:
- ack_no = X, window = 0
- ack_no = X, window = 8K
由于两个数据包具有相同的 ack_no,并且它们可以在传输过程中重新排序,发件人如何知道哪个先到?
如果发件人不知道哪个先来,那么在收到两个数据包后,如何知道是否允许发送?
一种猜测是,也许 window 的上限永远不允许下降?一旦接收方分配了接收缓冲区并通告了它,它就永远无法取消通告了吗?在这种情况下,可以通过以下代码可靠地处理 window 更新(为简单起见,假设没有 window 比例):
// window update (
int ack_delta = pkt_ack_no - c->tx_sn_ack;
c->tx_window = MAX(BE16(PKT.l4.window), c->tx_window - ack_delta);
if (c->tx_window)
Net_Notify(); // wake up transmission
但从接收器的角度来看,这很糟糕:它大大增加了可靠地支持 10K 连接所需的内存。协议肯定比那更聪明?
假设接收缓冲区永远不会缩小,这是有意未记录的,目的是创建一个精英“游戏中的皮肤”俱乐部,以限制 TCP 实现的数量。
original standard 说缩小 window 是“令人沮丧的”,但没有指出它不能可靠地工作:
The mechanisms provided allow a TCP to advertise a large window and to subsequently advertise a much smaller window without having accepted that much data. This, so called "shrinking the window," is strongly discouraged.
更糟糕的是,该标准实际上缺少问题中提出的 MAX 操作,如果确认号没有增加,则只从最近的数据包设置 window:
If SND.UNA < SEG.ACK =< SND.NXT, the send window should be updated. If (SND.WL1 < SEG.SEQ or (SND.WL1 = SEG.SEQ and SND.WL2 =< SEG.ACK)), set SND.WND <- SEG.WND, set SND.WL1 <- SEG.SEQ, and set SND.WL2 <- SEG.ACK. Note that SND.WND is an offset from SND.UNA, that SND.WL1 records the sequence number of the last segment used to update SND.WND, and that SND.WL2 records the acknowledgment number of the last segment used to update SND.WND. The check here prevents using old segments to update the window.
因此,如果重新排序具有相同 ack 编号的数据包,它将无法增长 window。
底线:实施实际有效的东西,而不是标准中的东西。