3

我每 10 秒在套接字上发送 2 个字节的应用程序数据(阻塞),但发送调用在最后一个实例中被阻塞超过 40 秒。

  • 2012-06-13 12:02:46.653417|信息|发送前
  • 2012-06-13 12:02:46.653457|INFO|发送后(​​2)
  • 2012-06-13 12:02:57.566898|信息|发送前
  • 2012-06-13 12:02:57.566962|INFO|发送后(​​2)
  • 2012-06-13 12:03:08.234060|信息|发送前
  • 2012-06-13 12:03:08.234101|INFO|发送后 (2)
  • **2012-06-13 12:03:19.010743|信息|发送前
  • 2012-06-13 12:04:00.969162|INFO|发送后 (2)**

机器(linux)上的 tcp 默认发送缓冲区大小为 65536。

2 个字节的数据用于与服务器进行心跳,服务器希望客户端至少每 15 秒发送一次 HB。

另外,我没有禁用 naggle 的算法。

问题是 - 发送呼叫能否被阻塞这么长时间(如 40 秒)?而且它只是偶尔发生,它是在运行近 12 小时后发生的。

我知道的发送调用应该只是将数据复制到 TCP 发送缓冲区。

每 10 秒调用一次发布。不,它不会逐渐减慢发送呼叫的速度。它突然发生一次,然后由于另一侧的套接字关闭,因此应用程序退出。

int publish(char* buff, int size) const {
      /* Adds the 0x0A to the end */
      buff[size]=_eolchar;

      if (_debugMode)
      {
          ACE_DEBUG((MY_INFO "before send\n"));
      }

      int ret = _socket.send((void*)buff, size+1);

      if (_debugMode)
      {
          ACE_DEBUG((MY_INFO "after send (%d)\n", ret));
          //std::cout << "after send " << ret << std::endl;
      }

      if (ret < 1)
      {
          ACE_DEBUG((MY_ERROR "Socket error, FH going down\n"));
          ACE_OS::sleep(1);
          abort();
      }
      return ret;
 }
4

2 回答 2

3

使用阻塞send()调用时,从应用程序的角度来看,您可以将远程 TCP 缓冲区、网络和本地发送 TCP 缓冲区视为一个大缓冲区。

也就是说,如果远程应用程序在从其 TCP 缓冲区读取新字节时出现延迟,最终您的本地 TCP 缓冲区将变得(几乎)满。如果您尝试send()使用溢出 TCP 缓冲区的新有效负载,则send()实现(内核系统调用)不会将焦点返回给您的应用程序,直到 TCP 缓冲区获得足够的空间来存储该有效负载。

达到该状态的唯一方法是远程应用程序没有读取足够的字节。测试环境中的一个典型场景是远程应用程序在断点处暂停... :-)

这就是我们所说的慢消费问题。如果您分享该诊断,则有多种方法可以解决该问题:

  1. 如果您可以控制远程应用程序,请使其足够,以免本地应用程序被阻塞。
  2. 如果您没有远程应用程序的控制权,那么可能有多个答案:
    • 根据您自己的需要,最多可以阻止 40 秒。
    • 如果不是这样,您需要使用send()系统调用的解锁版本。从这里开始,有多种可能的政策;我在下面描述一个。(等一下! :-) )

您可以尝试使用一个动态数组,该数组充当假发送 TCP FIFO,并在发送调用返回您时增长EWOULDBLOCK。在这种情况下,您可能必须使用select()系统调用来检测远程应用程序何时跟上步伐并首先向其发送看不见的数据。

您在这里拥有的简单功能可能有点棘手publish()(虽然在大多数网络应用程序中很常见)。您还必须知道,不能保证动态缓冲区会增长到您不再拥有任何可用内存的程度,然后您的本地应用程序可能会崩溃。“实时”网络应用程序中的一个典型策略是为缓冲区选择任意最大大小,在达到时关闭 TCP 连接,从而避免本地应用程序耗尽可用内存。明智地选择最大值,因为它取决于潜在的慢速消费者连接的数量。

于 2012-06-14T17:39:48.217 回答
1

The following(and more that I am not gonna mention now) are considered blocking system calls:
send, connect, recv, accept.

What this means is that they can block as far as they need till the specified job is done. So yes, send can block for 40 seconds and more, depending on how much time it takes to send the data; though I cannot know why it blocked that long in your specific case.

If you want to avoid this blocking, I advice you to read about asynchronous sockets and I/O. They MIGHT prove to solve part of your problem.

于 2012-06-14T17:20:12.777 回答