Windows下的奇怪的tcp死锁

时间:2010-05-12 21:34:42

标签: c++ windows sockets tcp deadlock

我们正在移动LAN上的大量数据,并且必须非常快速可靠地进行。目前我们使用在C ++中实现的Windows TCP。使用大(同步)发送比一堆较小(同步)发送更快地移动数据,但是经常会在大的时间间隔(.15秒)内死锁,导致整体传输速率骤降。这种僵局发生在非常特殊的情况下,这使我相信它应该完全可以预防。更重要的是,如果我们真的不知道原因我们真的不知道它不会发生在一段时间内使用较小的发送。谁能解释这个僵局?

死锁描述(好的,僵尸锁定,它没有死,但是停止15秒左右,然后再次启动)

  1. 接收方发送ACK。
  2. 发送方发送包含消息结束的数据包(设置推送标志)
  3. 对socket.recv的调用大约需要.15秒(!)才能返回
  4. 关于呼叫返回的时间,接收方发送ACK
  5. 最后发送来自发件人的下一个数据包(为什么要等待?tcp窗口很大)
  6. 关于(3)的奇怪之处在于,通常该调用根本不需要花费太多时间并且接收完全相同数量的数据。在2Ghz的机器上,有3亿条指令值得花时间。我假设呼叫没有(天堂禁止)等待收到的数据在返回之前被激活,所以ack必须等待呼叫返回,否则两者必须被其他东西延迟。

    当第二个数据包(同一个消息的一部分)到达1到2之间时,问题永远不会发生。这部分非常清楚地说明它与Windows TCP不会发回的事实有关无数据ACK,直到第二个数据包到达或200ms计时器到期为止。但是延迟小于200毫秒(更像是150毫秒)。

    第三个不合时宜的角色(在我看来真正的罪魁祸首)是(5)。发送肯定是在那之前调用好.15秒,但是数据永远不会在ack返回之前点击线路。对我来说,这是这个僵局中最离奇的部分。它不是tcp阻塞,因为TCP窗口很大,因为我们将SO_RCVBUF设置为类似500 * 1460(仍然在meg以下)。数据进入非常快(基本上有一个循环通过发送旋转数据)所以缓冲区应该几乎立即填充。 Msdn提到在决定发送命中何时使用各种“启发式”,并且已经挂起的发送+完整缓冲区将导致发送阻塞,直到数据到达线路(否则发送显然只是将数据复制到tcp发送缓冲区并返回)。

    Anway,为什么发送者在此期间实际上并没有发送更多数据.15秒暂停对我来说是最离奇的部分。上面的信息是通过wireshark在接收端捕获的(当然除了在文本文件中记录的socket.recv返回时间)。我们尝试将发送缓冲区更改为零并关闭发送方上的nagel(是的,我知道nagel不会发送小数据包 - 但我们尝试关闭nagel,以防这是未声明的“启发式”的一部分,影响消息是否会从技术上来说,如果缓冲区已满并且有一个未完成的ACK,那么微软的nagel就会发送一个小数据包,所以它似乎是一种可能性。

1 个答案:

答案 0 :(得分:3)

发送阻塞直到收到上一个ACK几乎可以肯定地表明TCP接收窗口已满(您可以通过使用Wireshark来分析网络流量来检查这一点。)

无论您的TCP窗口有多大,如果接收应用程序没有像到达那样快速处理数据,那么TCP窗口最终会填满。我们在这里谈得多快?接收方对数据做了什么? (如果您正在将接收的数据写入磁盘,那么您的磁盘很可能无法满足千兆网络的需求。)


好的,所以你有一个730,000字节的接收窗口,你正在以480Mbps的速度传输数据。这意味着完全填满你的窗口只需要12ms - 所以当接收端的150ms延迟发生时,接收窗口几乎立即填满并导致发送器停止。

因此,您的根本原因是计划接收过程的延迟时间为150毫秒。有许多事情可能导致这种情况(可能就像内核需要将脏页面刷新到磁盘以便为您的应用程序创建更多空闲页面一样简单);您可以尝试增加您的流程调度优先级,但不能保证这会有所帮助。