连接的UDP套接字收到ICMP端口不可达消息

3
基于UDP是无连接协议的前提,我曾认为主机是否启动不重要。但是,现在我正在进行测试时发现,当我“连接”我的UDP客户端套接字时,对该套接字的“写入”会返回一个错误,因为服务器已发送回一个ICMP端口不可达错误。
根据Stevens的《Unix网络编程》,“连接”UDP端口的目的基本上是从路由表中缓存条目,而不是为每个数据包创建新条目,这应该具有性能优势。
然而,这个ICMP数据包导致我失去了客户端套接字,这非常令人恼火。
有没有人能够解释一下为什么会这样?是否有任何已知的解决方法?
我正在使用一个第三方Java库,它没有考虑到这一点,只是断开连接,我可能不得不对其进行修改以重新连接,但在此之前,我希望在(Linux)操作系统级别上有些事情可以防止这种情况发生……所有关于套接字选项等的调查都没有结果。
编辑
总之,这是不可能的,修复代码是唯一的方法。
唯一的可能性似乎是配置iptables来阻止ICMP响应,但这是一个过于严厉的措施。

话虽如此,我不确定我理解你的问题。您尝试连接到一个不可用的端点套接字,并收到了ICMP错误。那么当您尝试进行“写入”时,为什么不会出现错误呢? - Alnitak
因为它是“无连接”的 - 实际上,如果我没有连接,我就不会得到这个错误。不要被“已连接”这个词所误导,因为在数据报文的上下文中它表示的并不是字面意思。就像我说的那样,连史蒂文斯本人也感到困惑。 - robert
让我们在聊天中继续这个讨论 - Alnitak
“套接字首次实现时为什么做出了这个设计决策,很少有人理解。”请参阅该书的8.10节。 - robert
1
下一段文字似乎不正确。它说:“recvfrom可以返回的唯一信息是errno值;recvfrom无法返回数据报中出错的目标IP地址和目标UDP端口号。”这很奇怪,因为address字段正好可以用于此,只是BSD的开发人员决定如果有ICMP错误就不填充它。WRS已经离开我们了,但我认识其中一位合著者,可以向他询问此事。 - Alnitak
显示剩余5条评论
2个回答

6

虽然UDP套接字不严格意义上是“连接的”,但进行connect()调用会为该套接字创建本地“状态”。

这种状态不仅允许系统缓存目标的当前路由条目,还意味着所有随后的输出操作都不需要指定目标 - 它们将使用在connect()调用中指定的目标。 它还确保内核会丢弃未来自“已连接”方的套接字的入站数据包。

此连接状态还使内核能够将与该套接字相关的错误(通过ICMP发出信号)传递给应用程序 - 未连接套接字不会接收到这些消息 - 它们是“发送并忘记”的。

就你离线提供的log4j代码而言,在这种情况下问题似乎完全在用户空间代码中。 当write调用发生IOException时,log4j的UDPAppender只是单方面丢弃了套接字,并且没有提供任何手段来检测该条件以便您可以修复它。


感谢您的所有帮助和专业见解。 - robert

-2

UDP在IP协议的上层。您收到的消息来自该层。 请参见https://en.wikipedia.org/wiki/User_Datagram_Protocolhttps://en.wikipedia.org/wiki/Internet_Control_Message_Protocol

因此从连接的角度来看,它是无连接的,这意味着您不会拥有持久的通道,所以不能保证数据包按顺序到达并且可能出现错误也不能立即识别。

但是仍然需要能够连接到某个东西,才能开始工作。只是它发生在更低的层次。

但内核仍将跟踪套接字,因此它可能会通知您它接收到的ICMP数据包,例如来自途中路由器的数据包。重点是,ICMP位于更低的层次。

更新:顺便说一下,我认为你可能会遇到另一个问题。你的路由器或者其他中间设备可能会忽略它不知道该将数据包转发到哪里这个事实,并且悄悄地丢弃数据包,而不是回复一个ICMP错误消息。


谢谢你的见解,丹。实际上,我在这个问题上已经有了很大的进展,看起来我们将不得不重写代码。请查看此链接以了解“连接”状态如何适用于UDP:http://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/ch08lev1sec11.html - robert
你不需要连接UDP套接字。你最后一段话没有意义。 - user207421
1
好的,让我稍微重新说一下... :) - Dan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接