“丢失”的 UDP 数据包(JBoss + DatagramSocket)
Posted
技术标签:
【中文标题】“丢失”的 UDP 数据包(JBoss + DatagramSocket)【英文标题】:"Lost" UDP packets (JBoss + DatagramSocket) 【发布时间】:2011-02-23 18:34:36 【问题描述】:我开发了一些基于 JBoss+EJB 的企业应用程序的一部分。我的模块需要处理大量传入的 UDP 数据包。我已经做了一些负载测试,看起来如果以 11ms 的间隔发送数据包,一切都很好,但是在 10ms 的间隔的情况下,一些数据包会丢失。在我看来这很奇怪,但我做了几次 10/11ms 间隔负载测试比较,结果总是相同(10 毫秒 - 一些“丢失”的数据包,11 毫秒 - 一切都很好)。
如果同步有问题,我希望它在 11 毫秒测试(至少丢失一个数据包,或至少一个错误的计数器值)的情况下也会可见。 因此,如果不是因为同步,那么我接收数据包的 DatagramSocket 可能无法按预期工作。
我发现接收缓冲区大小 (SO_RCVBUF) 的默认值为 57344(可能与底层 IO 网络缓冲区有关)。我怀疑,也许当这个缓冲区已满时,新传入的 UDP 数据报会被拒绝。我尝试将此值设置为更高,但我注意到如果我夸大其词,缓冲区将返回到其默认大小。如果它依赖于底层,我如何从 JBoss 级别找出某些操作系统/网卡的最大缓冲区大小?
这可能是由接收缓冲区大小引起的,还是 57344 值足以处理大多数情况?您对此类问题有任何经验吗?
我的 DatagramSocket 上没有设置超时。我的 UDP 数据报包含大约 70 个字节的数据(不包括数据报头的值)。
[已编辑] 我必须使用 UDP,因为我收到 Cisco Netflow 数据 - 它是网络设备用来发送一些流量统计数据的协议。另外,我对发送的字节格式没有影响(例如,我不能为数据包添加计数器等等)。预计不会处理所有数据包(一些数据报可能会丢失),但我希望我会处理大部分数据包。在 10 毫秒间隔测试期间,大约 30% 的数据包丢失。
缓慢的处理不太可能导致此问题。目前单例组件在循环中保存对 DatagramSocket 调用接收方法的引用。收到数据包后,将其传递到队列中,并由从池中选择的无状态组件进行处理。 “Facade” Singleton 只负责接收数据包并将其传递给处理(它不会等待处理完成事件)。
提前致谢, 彼得
【问题讨论】:
为什么需要 UDP?我会使用 TCP,直到您的分析表明需要转到 UDP 的饱和层。此外,我对 UDP 的经验是数据通常是重复的。 “这是目前的状态。”因此,如果您错过了这个数据包,请不要担心,因为很快就会有另一个数据包! “缓慢的处理不太可能导致此问题。” --我认为是,因为 10 毫秒很小,与线程量子的持续时间大致相同。 这 10 毫秒可能因不同的操作系统/处理器而异。你可以减少发送数据的频率,比如 100 毫秒吗? @ChrisW 也许你是对的,但如果是这样,在 11ms 间隔的情况下是否也应该看到数据包丢失? (我不能观察任何人)@Jeff Storey 它只取决于外部网络设备发送数据包的速度。我只负责接收和处理。 【参考方案1】:UDP 不保证传递,因此您可以调整参数,但不能保证消息会得到传递,尤其是在数据传输量非常大的情况下。
如果你需要保证送达,你应该使用 TCP 来代替。
如果您需要(或想要)使用 UDP,您可以使用数字对每个数据包进行编码,并发送预期数量的数据包。例如,如果您发送了 10 个大数据包,您可以包含以下信息:数据包 1/10、数据包 2/10 等。这样您至少可以判断您是否没有收到所有数据包。如果您还没有收到它们,您可以发送请求以重新发送那些丢失的数据包。
【讨论】:
是的,一旦您有大量传输,数据包就会通过 MTU。在这种情况下,数据包会变成碎片。使用 UDP,甚至不能保证按顺序接收片段,但如果不是这种情况,底层协议 (IP) 将丢弃数据包,因此 UDP 永远不会自行接收数据包。这就是为什么使用 UDP 的大数据包的可靠性要低得多的原因——因为 UDP 保留了边界,所以在幕后进行了更多的丢弃:) 感谢您的回答,不幸的是我对数据包的生成方式没有影响(我也无法切换到 TCP) - 请查看我编辑的帖子。【参考方案2】:UDP 本质上是不可靠的。
数据报可以在发送者和接收者之间的任何点被丢弃,即使在接收者中低于您的代码级别。将 recv 缓冲区设置为更大的大小可能有助于机器内的网络代码缓冲更多数据报,但您应该预料到无论如何都会丢失一些数据报。
如果您的 recv 逻辑花费的时间太长(即比新数据报到达所需的时间长),那么您将永远落后,最终总会错过数据报。您所能做的就是确保您的 recv 代码尽可能快地运行,也许将入站数据报移动到队列并“稍后”或在另一个线程上处理它,但这只会将您的问题转移到您有一个不断增长的队列。
[重新编辑...] 什么正在处理您的队列以及生产者和消费者之间的锁定如何工作?更改您的代码,以便 recv 逻辑简单地增加计数并丢弃数据并循环回来,看看您是否丢失了更少的数据报;无论哪种方式,UDP都是不可靠的,您将拥有被丢弃的数据报,您应该期待并处理它。担心它意味着你关注的是错误的问题;利用你得到的数据并假设你不会得到很多数据,然后即使网络拥塞并且你的大部分数据报被丢弃,你的程序也能正常工作。
总之,UDP 就是这样。
【讨论】:
感谢您的回答。 Producer 持有对 DatagramSocket 的引用,producer 是 JBossEJB3ext @Service bean(单例)。它调用 while(true) 循环阻塞接收方法。当它接收到数据包时,DatagramSocket 通过 AsyncUtils(异步调用)传递到池中的无状态 bean 之一。我会尝试禁用处理并只计算传入的数据包,然后我会给出反馈。【参考方案3】:在您的测试中,缓冲区中最多只能有两个数据包,因此如果每个数据包小于 28KB,这应该没问题。
您知道 UDP 是有损的,但您应该能够每 10 毫秒发送多个数据包。我建议你编写一个简单的接收器,它只监听数据包以确定它是你的应用程序还是网络/操作系统级别的东西。 (我怀疑是后者)
【讨论】:
有什么方法可以知道缓冲区溢出。因为我连续接收数据包,并且在长时间运行后它停止接收。是不是因为缓冲区溢出。 @GeorgeThomas 接收者不知道数据包是否丢失或为什么丢失。您只能通过实验来确定这一点。例如玩数据包之间的大小或距离。 我不知道!我在 android 中实现了一个 udp 侦听器,我每 1 秒接收一次数据包。一切正常,但工作一段时间后它完全停止。我会尝试改变大小和距离并试一试 @GeorgeThomas 您可能需要在可以监控 UDP 数据包的地方进行测试。例如使用wireshark。 UDP 数据包应该停止是没有充分理由的。您应该只会看到随机数据包在负载下丢失。 服务器仍在发送我猜是因为 ios 应用程序正在成功接收数据包,所以我认为它不是服务器的问题。【参考方案4】:我不了解 Java,但是……API 是否允许您为数据报调用异步侦听/接收:
使用 O/S API 进行接收(将应用程序级缓冲区作为参数传递) (等待没有收到任何东西......) (O/S 从网络接收一些东西...) O/S 将接收到的数据包放入缓冲区并完成/返回您的 API 调用如果是这样,那么我建议您执行几个并发的 API 调用实例,以便有多个并发的应用程序级缓冲区可以接收多个数据包。
【讨论】:
感谢您的回答。目前,使用单例外观 - 它包含对 DatagramSocket 的引用。它在循环中调用 receive() 方法 - 它是阻塞操作。如果接收到数据报,则将其传递给处理组件(池化无状态 bean)。容器管理同步。在我看来,这种方法可以防止同步问题并提供可扩展性。只允许一个 DatagramSocket 实例(每个 IP 和端口绑定)。 @Piotrek 我建议你尝试改变这一点:而不是一个同步/阻塞操作,尝试做多个异步/非阻塞操作......这样 O/S 就有多个并发应用程序-接收多个数据包的级别缓冲区。以上是关于“丢失”的 UDP 数据包(JBoss + DatagramSocket)的主要内容,如果未能解决你的问题,请参考以下文章
使用 esp32 接入点流式传输 UDP 数据包会导致大量数据包丢失