为什么 TCP 允许 ACK 值和发送时不一致?

2022-10-24 20:15:03 +08:00
 iqoo

...

[发送者 -> 接收者] seq=8000 ack=50

[发送者 -> 接收者] seq=9000 ack=50

[接收者 -> 发送者] seq=50 ack=8700 (小于发送者的边界值)

正常情况下,接收者的协议栈会发送 seq=9000 ,确认收到,并让对方发送后续数据。

但事实上回复的 ack 值小于 9000 ,对方也会继续发送。虽然很久之前就知道这个特性,并且该特性会带来不少安全问题,但不知为何要这样设计,而且至今很多系统仍支持。

2502 次点击
所在节点    程序员
12 条回复
tftk
2022-10-24 20:49:34 +08:00
确定后面没有 ack 了或者没有丢包吗
Chieh
2022-10-24 21:37:57 +08:00
正常情况下,接收者应该发送 seq=ack 吧
wangyu17455
2022-10-24 22:04:17 +08:00
发送方没触及发送窗口右边界和接收方接收窗口中较小的那个就会一直发送,收到 ack 会导致发送窗口右移,只要发送窗口右边界大于 9000 就会继续发送
lysS
2022-10-24 22:55:09 +08:00
安全问题是啥? tcp 也不是安全的吧?
proxytoworld
2022-10-24 23:58:15 +08:00
没有到临界值把,等差值到了临界值就会少发
bantianys
2022-10-25 07:19:23 +08:00
建议确认下对应的发送包和接受包对应关系,可以参考下 wireshark 抓包界面的 symbol 。
参考 https://www.wireshark.org/docs/wsug_html_chunked/ChUsePacketListPaneSection.html

Table 3.16. Related packet symbols
参考 The selected packet acknowledges this packet 图标
Panic
2022-10-25 08:53:35 +08:00
因为 tcp 是 stream 类型的,stream ,stream ,stream 。
字节流本来就允许这样的啊。
zhs227
2022-10-25 09:34:14 +08:00
链路上有包正在传送,称为 in flight 。如果发一个,ack 了再发下一个,那路上永远只有 1 个包大小的数据。无法有效利用带宽。1 个包的 size / rtt ,你猜能达到多高的传送带宽。

如果收到 ack 小于当前发送的最后包,发送端就不再发送,相当于把窗口彻底去掉了,不清楚你说的带来不少安全问题是什么安全问题。

如果全世界都错了,……
iqoo
2022-10-25 11:21:57 +08:00
@zhs227 合法的 ACK 值是 8000 或 9000 ,是已发送的 SEQ 中的某一个,而不该是 8700 这个中间值。除非是链路中的设备把原先的包拆分了。
zhs227
2022-10-25 11:39:09 +08:00
tcp 是 stream,没有边界的。 你调用 tcp 的 send 函数不是按包发送,只是进入操作系统缓冲区,然后缓冲区再分段向外发送。操作系统向外发送的分段不一定与上层调用 send 是一致的。比如说你调用 send 每次发送包长是 9000 字节,但是网络上的 MTU 只有 1500 ,它就会分成很多个包。所以没有“合法”的 ack 值的说法
nVic
2022-10-25 13:25:47 +08:00
JohnBull
2022-10-25 13:37:03 +08:00
问题是"为什么不允许呢?"
TCP 的 ACK 的含义本来很简单:"我已经成功收到了你的第 8700 字节,请从 8701 字节继续发"
作为面向字节流的协议设计,语义简洁而明确. 如果引入了与发送分包相关的限制就是无事生非了,让人根本没法实现

出现这种问题的直接原因不清楚, 最有可能是接收方的缓冲区只够接收到 8700 字节了.

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/889494

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX