长话短说:在Linux中,如何确保收到某个TCP数据包的ACK消息?
全文:
我正在调试Asterisk / OpenH323< - >松下IP-GW16问题。
H323连接涉及两个会话:H225.0和H245。这些只是两个TCP会话,传输一些数据。
我们称他们为Session 1
(针对H225.0)和Session 2
(针对H245)。
Session 1
众所周知的TCP端口号为1720,而Session 2
的端口是在运行时选择的。
控制流程如下:
Session 1
(TCP / 1720),并通过Session 1
发送一条SETUP消息,其中包含Panasonic将收听的port 2
。Session 1
port 2
Session 1
发送TCP ACK。Session 2
上打开TCP port 2
。步骤2和3的顺序很重要:除非port 2
收到CALL PROCEEDING消息,否则Panasonic不会收听step 2
。
但在OpenH323代码中,step 2
和step 5
距离我们只有几行。
这就是为什么连接sometimes
在调试模式下工作而quite never
在发布中工作的原因。
在数据包转储中可以清楚地看到它。我进行了一系列实验,在52个中的52个案例中,如果step 5
在step 4
之前,则连接失败;如果没有,连接成功。
除了step 4
中的ACK之外,没有其他消息从Panasonic发送,并且似乎Asterisk可能知道听到port 2
的唯一方式是收到该ACK。
当然我可以实现定时等待,但我想要一个更清洁的解决方案。
所以问题再次出现:在step 2
中通过TCP连接发送消息后,如何知道是否收到包含该消息的数据包的ACK?
答案 0 :(得分:3)
在这种特定情况下,我会说您会发现tcp_info
结构会保留非零tcp_info.tcpi_unacked
。你可以通过getsockopt(TCP_INFO)
得到这个。
注意:界面不稳定。
答案 1 :(得分:1)
TCP级别的ACK由操作系统发送,并且可以在进程读取数据之前发送。因此,如果您收到ACK,则并不意味着远程应用程序对该消息采取了行动,或者甚至通知它已存在。
想象一下:如果TCP确认我们是消息确认,那么应用程序应该读取()消息,处理消息(可能需要一段时间),然后调用“read_ok”系统调用。据我所知,使用标准套接字API是不可能的。
您可以使用SIOCOUTQ ioctl(man 7 tcp)检查是否有任何未包装的数据。但这不是你问题的可靠解决方案。
你确定h323应该如何工作吗?如果port2无效或被另一个连接占用怎么办?应该发回确认或错误。
答案 2 :(得分:0)
虽然松下正在使用可能解释它的专有操作系统,但时序似乎很奇怪。
澄清 - AIUI - 如果松下正在运行“正常”操作系统,那么它在第4阶段发送的ACK将在Panasonic的软件具有来自控制TCP套接字的read()
数据之后立即发生。
类似地,对write()
(在步骤2中)的OpenH323代码调用不应返回(假设它不是非阻塞套接字!),直到来自Panasonic的ACK为止由Asterisk服务器收到。 那是您应该知道已收到ACK的方式。
基本上似乎松下在listen()
read()
消息之后才在第二个套接字上执行相当于CALL PROCEEDING
的操作。它看起来像竞争条件 - 有时Open323会在另一端准备好之前尝试connect()
。
当发生这种情况时,你会在OpenH23结束时得到ECONNREFUSED
吗?