【问题标题】:In Linux, how do I know if an ACK is received to a certain TCP packet?在 Linux 中,我如何知道某个 TCP 数据包是否收到了 ACK?
【发布时间】:2010-10-01 09:13:11
【问题描述】:

长话短说:在 Linux 中,如何确保收到某个 TCP 数据包的 ACK 消息?

全文:

我正在调试 Asterisk/OpenH323 Panasonic IP-GW16 问题。

H323 连接涉及两个会话:H225.0 和 H245。这只是两个 TCP 会话,通过它们传输一些数据。

我们称它们为 Session 1(用于 H225.0)和 Session 2(用于 H245)。

Session 1 具有众所周知的 TCP 端口号 1720,而Session 2 的端口是在运行时选择的。

控制流程如下:

  1. Panasonic 调用 Asterisk:它打开 Session 1 (TCP/1720) 到 Asterisk 并通过 Session 1 发送一条 SETUP 消息,其中包含 Panasonic 将侦听的 port 2
  2. Asterisk 通过Session 1 向 Panasonic 发送 CALL PROCEEDING 消息
  3. 松下开始收听port 2
  4. Panasonic 通过Session 1 发送 TCP ACK。
  5. Asterisk 在port 2 上打开 TCP Session 2

第 2 步和第 3 步的顺序很重要:Panasonic 不会收听 port 2,除非它在 ​​step 2 上收到 CALL PROCEEDING 消息。

但在 OpenH323 代码中,step 2step 5 仅相隔几行。

这就是连接sometimes 在调试模式下工作而quite never 在发布模式下工作的原因。

在数据包转储中可以清楚地看到。我做了一系列的实验,52个案例中有52个,如果step 5step 4之前,连接失败;如果没有,则连接成功。

除了step 4 中的 ACK 之外,松下没有发送任何其他消息,而且似乎 Asterisk 知道 port 2 被监听的唯一方法是接收该 ACK。

当然我可以实现定时等待,但我想要一个更简洁的解决方案。

那么问题又来了:在step 2 中通过 TCP 连接发送消息后,我如何知道是否收到了对包含该消息的数据包的 ACK?

【问题讨论】:

  • 被确认的消息不会告诉您由于接收到数据而发生了任何处理,只是表明 TCP/IP 堆栈已接收到数据。所以你试图测试错误的东西。
  • @DavidSchwartz 这是一个老问题,但出于好奇,我应该测试什么是正确的?我正在处理的是一个糟糕的旧硬件,它运行着一个未记录的自定义操作系统,而 ACK 是告诉另一个连接被监听的唯一方法,而且从经验上讲,这是一个非常可靠的方法。
  • @Quassnoi 如果操作系统没有记录和自定义,那么您很可能会倒霉。

标签: linux tcp


【解决方案1】:

在这种特定情况下,我会说您会发现您的tcp_info 结构将包含一个非零tcp_info.tcpi_unacked。你可以通过getsockopt(TCP_INFO) 得到这个。

注意:界面显然不稳定。

【讨论】:

    【解决方案2】:

    虽然 Panasonic 使用专有的操作系​​统可能会解释它,但时序似乎很奇怪。

    澄清一下 - AIUI - 如果 Panasonic 正在运行“正常”O/S,它在第 4 阶段发送的 ACK 将在 Panasonic 的软件从控制 TCP 套接字获得read() 数据后立即发生。

    类似地,对 write() 的 OpenH323 代码调用(在第 2 步中)不应该返回(假设它不是非阻塞套接字!)直到 Panasonic 收到 ACK星号服务器。 是您应该知道已收到 ACK 的方式。

    从本质上讲,松下似乎没有在第二个套接字上执行与 listen() 等效的操作,直到它具有 read() CALL PROCEEDING 消息之后。这看起来像是一种竞争条件 - 有时 Open323 会在另一端准备好之前尝试connect()

    发生这种情况时,您是否在 OpenH23 结束时收到ECONNREFUSED

    【讨论】:

    • socket 是阻塞的,所有这些东西都在一个线程中。我以为send() 仅在缓冲区已满时才阻塞,我错了吗?我没有寻找确切的代码,但是松下发送了一个 RST/ACK 数据包来回复 SYN,所以我认为是的,我得到了一个 ECONNREFUSED
    • 好的 - 这有点错误 - 我会编辑。一旦 O/S 收到数据包,通常会发送 ACK,它不依赖于应用程序读取()数据。
    • 写调用可以在所有数据被确认之前返回。
    【解决方案3】:

    TCP 级别的 ACK 由操作系统发送,可以在进程读取数据之前发送。因此,如果您收到 ACK,这并不意味着远程应用程序对消息采取了行动,或者甚至被告知它的存在。

    想象一下:如果 TCP 确认我们是消息确认,那么应用程序应该读取()消息,处理它(这可能需要一段时间),然后调用“read_ok”系统调用。据我所知,使用标准套接字 API 这是不可能的。

    您可以使用 SIOCOUTQ ioctl (man 7 tcp) 检查是否有任何未确认的数据。但这不是解决您问题的可靠方法。

    您确定这就是 h323 的工作方式吗?如果 port2 无效或被另一个连接占用怎么办?应发回确认或错误。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-10-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-02
      • 1970-01-01
      相关资源
      最近更新 更多