UDP发送到

时间:2016-12-19 21:25:59

标签: udp sendto econnrefused

我正在使用正在编写的应用程序遇到一些无法解释的行为 UDP数据与sendto()到多个端口(全部用套接字打开(PF_INET, SOCK_DGRAM,0))为了一组客户端阅读过程的好处。这些sendto()偶尔会无法预测地触发 ECONNREFUSED 错误。这发生在macOS Sierra(10.12)系统上,其sendto(2)手册页甚至没有列出 ECONNREFUSED 作为可能的错误。有趣的是,我有一个CentOS7系统(这些错误永远不会发生),其sendto(2)手册页引用了udp(7)手册页上记录的其他sendto()错误,并且CentOS7 udp(7)页面说:

ECONNREFUSED

 No receiver was associated with the destination address. This
 might be caused by a previous packet sent over the socket.

ECONNREFUSED 未在macOS Sierra udp(4)页面的任何位置提及。) 我不知道CentOS7手册页是否与macOS有任何关联, 但是假设他们做了一下,关于sendto()的 ECONNREFUSED 的上述解释在几个方面令人困惑:

首先,我听过的关于UDP的一切都强调了它的无连接性。那么,为什么sendto()会失败,因为没有连接接收器(或者关联',正如手册页所说的那样,我认为是相同的东西)?不是UDP的全部意义,如果你是一个说话者,你只是喋喋不休,不关心其他人是否在听?然而,这些CentOS7 udp(7)注释似乎适用于我的Sierra系统,因为当我运行的客户端进程被绑定并从这些端口读取时我从来没有遇到过问题,但如果我开始 在读者运行之前的UDP编写器我经常(但不总是) 看到这些错误。

其次,任何人都可以向我解释为什么,根据CentOS7 udp(7) 文档,通过套接字发送的先前数据包可能导致no 接收器与目标地址相关联?这没有 对我来说很有意义。一些数据报是否只是如此有毒,以至于他们会杀死任何阅读它们的人?

我还应该注意到,除了没有在CentOS7上看到这个问题 实际上(如果含糊地)记录在案的地方,我也从未在Sierra之前的任何MacOS版本上体验过它,而且这段代码在我这里已经运行了很多年。我仍然有一个El Capitan系统,无法复制那里的错误。

以下是有关我的应用的更多信息 - 请随时发表评论 要么是关于PF_INET UDP,sendto()和 ECONNREFUSED 的上述一般性问题,要么是关于我的应用程序的更具体的细节,如下所述。我已经有了一个可用的解决方法(见下文),但我想更好地了解发生了什么。

我的应用程序正在从各种来源(串行线路和/或UDP端口)读取数据,将其按摩到各种类型的重新格式化的输出消息,然后将这些消息写入多个预定义的连续编号(例如,3000到3004)UDP相同IP地址的端口,由少量可变数量的客户端读取(限制为5但通常不超过3或4)。每个客户端扫描我的应用程序的UDP输出端口的预定义列表,绑定到第一个可用端口,然后从该端口执行所有读取操作。我无法保证我的编写器应用程序和多个阅读器进程的启动顺序(这是我的问题的核心部分)。我的应用程序每秒向每个输出端口写入一次消息,每个输出端口通常不超过80个字节(所有ASCII文本)。

这些阅读器客户端可能正在运行(i)与我的应用程序相同的本地主机,(ii)单个远程主机,或(iii)本地网络上的不同远程主机,因此我的编写器应用程序接受任意IPv4目标address作为命令参数。假设我的编写器在主机192.168.1.LLL(本地主机)上运行,最常用的目标地址将是:

  • 127.0.0.1
  • 192.168.1.LLL(localhost的实际外部地址)
  • 192.168.1.RRR(同一局域网上的某些远程主机)
  • 192.168.1.255(多个远程主机上的读者的本地广播)

请注意,仅在将输出发送到127.0.0.1时才会看到这些错误 或192.168.1.LLL,localhost的实际外部地址。该 当我写入特定的远程主机时,永远不会发生错误 192.168.1.RRR或LAN的广播地址192.168.1.255。在那儿 应该是本地PF_INET与远程PF_INET UDP写入之间的区别?也许本地写入必须在某个本地缓冲区内以特定方式处理,这些缓冲区受到各种约束,而在主机之外发送的数据包只是分散到风中,发生的任何事情都被认为超出了本地sendto()的报告容量?虽然我在使用广播地址192.168.1.255时从未发现过这些错误,但我不想在网络礼貌中使用它,除非我知道我的客户端确实在多个远程主机上运行 - 如果一切都在一个系统上我就不会这样做而是使用严格的本地地址127.0.0.1或192.168.1.LLL(这些地址可能导致错误)保持私有。

目前我只是忽略所有 ECONNREFUSED sendto()错误来解决这个问题。看起来我倾向于在启动我的应用程序的几秒钟内得到它们,虽然从不在每个端口上的第一个sendto()上,并且通常只在我的5个输出端口中的一个上(尽管生成错误的端口并不总是相同的)。并且,在最初的错误之后,接下来的几分钟'尽管仍然没有读者在运行,但是输出的价值(我观看过的最长时间)是没有错误的。然而,这些错误是令人费解的,我希望能够更好地理解它们,以使我的代码尽可能健壮。我没有在这篇文章中包含我的实际代码,因为后者已经过长了,据我所知,代码并没有什么异常,但如果有用的话我可以单独发布。

谢谢!

罗杰戴维斯, 大学。夏威夷

1 个答案:

答案 0 :(得分:2)

虽然在UDP层,您可以加入任何IP。 RFC1122,第4.1.3.3节,表明IP层的任何错误(导致ICMP错误发生)必须将错误传播回应用层。正如您在RFC792第3页中看到的,代码3消息是Port Unreachable

因此,无法将IP数据包发送到127.0.0.1的端口将导致icmp错误在应用层显示为ECONNREFUSED。它被报告为异步(因为icmp有回复超时),你可能已经发送了另一个udp数据包。

为什么它会在本地连接上发生更多?数据包实际上并没有离开内核,因此它可以在发送下一个udp数据包之前回复ICMP错误。在其他地址,它实际上必须放在电线上。因此,您仍然可以获得错误,但根据您的UDP发送速率,它们的频率会降低。此外,如果您通过网关发送网关可能只丢弃udp数据包。如果您的主机和远程主机之间存在防火墙,它也可能会丢弃icmp回复,或限制回复的返回率。

解决错误,如果你确实得到ECONNREFUSED,你知道没有主机拥有该IP或没有任何东西正在侦听该端口。无论哪种方式,它仍然无意义发送。