【问题标题】:tcp - getting num bytes ackedtcp - 得到 num 个字节的确认
【发布时间】:2010-10-12 07:19:28
【问题描述】:

在标准的 tcp 实现中(例如,在 bsd 上),有没有人知道是否有可能找出远程主机已经确认了多少字节?在套接字上调用 write() 会返回写入的字节数,但我相信这实际上意味着可以放入 tcp 缓冲区的字节数(不是写入网络的字节数,或确认的字节数)。或者我错了……

谢谢!

【问题讨论】:

    标签: c tcp


    【解决方案1】:

    当你有 NODELAY=false (这是默认值)时,当你用比 TCP 窗口少的字节调用 send() 时,字节并没有真正立即发送,所以你是对的。操作系统会稍等片刻,看看你是否调用了另一个 send(),以便只使用一个数据包来传输组合数据,并避免浪费 TCP 标头。

    当 NODELAY=true 时,数据会在您调用 send() 时传输,因此您可以(理论上)依靠返回值。但由于增加了网络效率低下,不建议这样做。

    总而言之,如果不需要绝对精度,即使 NODELAY=true,也可以使用 send() 返回的值。该值不会反映即时现实,但会在几毫秒后反映(但还会检查丢失的连接,因为您发送的最后一个数据块可能已丢失)。一旦连接正常终止,您就可以相信所有数据都已传输。如果不是,您之前就会知道 - 要么是因为连接突然断开,要么是因为您收到了与数据保留相关的错误(或任何其他错误)。

    【讨论】:

    • 当然,send 返回的值只是放入输出缓冲区的字节数,因此您可以使用此数字知道接下来要发送哪些字节,以防所有字节都无法容纳。这不是实际传输或确认的字节数,或者类似的东西,是吗?
    • 不,还没有。但是如果你调用 send() 两次,两次调用之间有足够的时间(超过发送延迟时间,通常是 200 毫秒),并且第二次调用没有错误返回,你可以确定第一次调用中的所有字节都是已发送。
    • 你有任何证据吗?如果将字节放入输出缓冲区,则发送调用成功。它所证明的只是发送缓冲区足够大。它没有说明线路上的流量。例如,如果没有收到来自另一端的确认,TCP 堆栈将在放弃之前缓慢地进行多次重传。在此期间,后续发送调用不会失败,除非发送缓冲区已满。
    • 你说得对,我忘了说没有收到ack时的重传时间。虽然我不会称之为“缓慢”——时间是由操作系统动态调整的,对于现在的普通连接,我认为它不会超过 100 秒毫秒,或者在最坏的情况下会超过几秒。但我的观点是,如果您等待足够长的时间并且连接仍然存在,您可以假设之前发送的字节已被传输。
    • 您是对的,初始 TCP 重传在典型的快速网络上发生得非常快。但是,如果链路出现故障,即使在快速网络上,操作系统也需要多次重传才能放弃。例如:pcvr.nl/tcpip/tcp_time.htm。 (除非本地发生连接问题,否则当操作系统知道根本没有连接时。)
    【解决方案2】:

    我不知道有什么方法可以得到这个,而且它可能对你没有用。

    假设您想知道主机接收了多少数据,以便在连接丢失并重新连接后,您可以再次从那里开始发送。因此,已确认的数据仅由操作系统确认!它并不表示你的程序在另一端收到了哪些数据;根据那里的 TCP 接收缓冲区的大小,您的程序可能落后数百 KB。如果您想知道那里的程序已接收到多少数据“使用”,然后让它发送应用程序级 ACK

    【讨论】:

    • Hmph - 总是有人说“你真的不想要那个”......我的用例有点平淡:我想在将大文件从手机上传到时显示进度条服务器。有了这样一个频道 - 巨大的窗口,巨大的 rtt,让 tcp 告诉我这些信息是有道理的。
    • :-) 好的,在那种情况下......拥有这样的价值会很好 - 但我想人们会滥用它。 :-,( 我想我只是习惯了新手要求的超出可能的范围。(我想我可以将他们推荐给例如joelonsoftware.com/articles/LeakyAbstractions.html
    【解决方案3】:

    我认为你错了,尽管这是我想先看看具体实施的地方之一,然后我才会赌上大笔的钱。但是,请考虑 TCP 连接的情况,在原始握手后立即断开连接。如果返回的字节数只是缓冲的数量,则可能显然已经写入了许多字节,但它们仍未传递;这将违反 TCP 的交付保证属性。

    但请注意,这仅适用于 TCP;并非 IP 中的所有协议都提供相同的保证。

    【讨论】:

    • TCP 保证交付或失败通知。沿着管道发送一堆字节,然后立即关闭套接字会导致一些字节“丢失”。因此 SO_LINGER 选项的原因是,它将保持套接字活动,直到发送所有字节(大部分时间)。
    【解决方案4】:

    通过使用ioctl(fd, TIOCOUTQ, &intval); 将传出队列放入intval,您可能对TCP 有一些运气。这将是保留在队列中的总长度,包括“由应用程序写入”但尚未发送。这仍然是我目前能想到的最佳近似值。

    【讨论】:

    • 德拉特。 -1(错误);在 bsd 上不可用(呃,iPhone,就是这样)。在我看来,tcp 堆栈应该有这些信息。为什么它不以任何方式报告它?也许有更深层次的原因,但我想不出来。感谢您的帮助!
    • 德拉特是对的。我应该提到 TIOCOUTQ(和 TIOCINQ)并非在所有地方都受支持,即使是你返回的数字在每个平台上也并不总是意味着同样的事情。
    猜你喜欢
    • 1970-01-01
    • 2017-03-07
    • 1970-01-01
    • 2013-08-06
    • 1970-01-01
    • 1970-01-01
    • 2013-01-18
    • 2013-11-17
    • 2017-01-12
    相关资源
    最近更新 更多