对于会话的每个方向,TCP 会跟踪发送的字节数和接收的字节数。这是使用序列号(跟踪发送的字节)和确认号(跟踪接收的字节)完成的。
为简单起见,我将继续处理,就好像 SEQ# 和 ACK# 是发送的数据包计数(实际上,它是发送的字节计数)。此外,我们只会跟踪一个方向的对话,我们不会打扰另一个方向的对话。
Bob Alice
Sends packets #1, #2, #3 --->
Bob 向 Alice 发送带有 SEQ# 1、2 和 3 的数据包.... Alice 可以如何响应有以下三种可能性:
如果 Alice 收到每个数据包,她将回复 ACK#4,表示她已经收到了 #4 之前的所有数据,并准备好接下来的 #4。
如果 Alice 只收到#1,没有其他任何东西,她将用 ACK#2 响应,表明她已准备好接收#2,并且之前已经收到了所有信息。这将提示 Bob 再次发送 #2 和 #3。
如果 Alice 只收到 #1 和 #3,并且不知何故 #2 在传输过程中丢失了。Alice 仍然别无选择,只能用 ACK#2 响应。请记住,Alice 只能用一个确认号码来响应,以确认她收到的数据包。如果她回复 #4,它会通知 Bob 收到了 #1、#2 和 #3,但事实并非如此。
因此,在这两种情况下之前,Bob收到ACK#2,和Bob没有办法知道是否方式只是#2或#2和#3丢失了-所以为了安全起见,鲍勃将重新发送#2,#3。
也就是说,在上面的第 3 种情况下,当 Bob 重新发送 #2 和 #3,但 Alice 已经有了 #3..简单地忽略接收到的重复 #3 完全取决于实现——并且在很大程度上,用户不会注意到差异。最终效果是一样的。
当然,这只是因为只有两个数字被跟踪,一个 SEQ# 表示发送的字节,一个 ACK# 跟踪接收的字节。正如 OP 所指出的,TCP 添加了一项名为 Selective Acknowledgement 或TCP SACK 的功能。
TCP SACK 允许接收方不仅发送 ACK#(接收到的字节数),而且还指定接收到的字节的“左边缘”和“右边缘”。然后向发送方具体指示哪些字节需要重新发送。