V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
iqoo
V2EX  ›  程序员

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

  •  
  •   iqoo · 2022-10-24 20:15:03 +08:00 · 2476 次点击
    这是一个创建于 766 天前的主题,其中的信息可能已经有所发展或是发生改变。

    ...

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

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

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

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

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

    12 条回复    2022-10-25 13:37:03 +08:00
    tftk
        1
    tftk  
       2022-10-24 20:49:34 +08:00
    确定后面没有 ack 了或者没有丢包吗
    Chieh
        2
    Chieh  
       2022-10-24 21:37:57 +08:00
    正常情况下,接收者应该发送 seq=ack 吧
    wangyu17455
        3
    wangyu17455  
       2022-10-24 22:04:17 +08:00
    发送方没触及发送窗口右边界和接收方接收窗口中较小的那个就会一直发送,收到 ack 会导致发送窗口右移,只要发送窗口右边界大于 9000 就会继续发送
    lysS
        4
    lysS  
       2022-10-24 22:55:09 +08:00
    安全问题是啥? tcp 也不是安全的吧?
    proxytoworld
        5
    proxytoworld  
       2022-10-24 23:58:15 +08:00
    没有到临界值把,等差值到了临界值就会少发
    bantianys
        6
    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
        7
    Panic  
       2022-10-25 08:53:35 +08:00
    因为 tcp 是 stream 类型的,stream ,stream ,stream 。
    字节流本来就允许这样的啊。
    zhs227
        8
    zhs227  
       2022-10-25 09:34:14 +08:00
    链路上有包正在传送,称为 in flight 。如果发一个,ack 了再发下一个,那路上永远只有 1 个包大小的数据。无法有效利用带宽。1 个包的 size / rtt ,你猜能达到多高的传送带宽。

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

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

    出现这种问题的直接原因不清楚, 最有可能是接收方的缓冲区只够接收到 8700 字节了.
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1349 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 17:43 · PVG 01:43 · LAX 09:43 · JFK 12:43
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.