【问题标题】:Java dropping half of UDP packetsJava丢弃了一半的UDP数据包
【发布时间】:2010-03-14 01:41:11
【问题描述】:

我有一个简单的客户端/服务器设置。服务器是 C 语言,而查询服务器的客户端是 Java。

我的问题是,当我通过连接发送带宽密集型数据(例如视频帧)时,它会丢弃多达一半的数据包。我确保在服务器端正确地对 udp 数据包进行分段(udp 的最大有效负载长度为 2^16)。我验证了服务器正在发送数据包(printf sendto() 的结果)。但是 java 似乎并没有得到一半的数据。

此外,当我切换到 TCP 时,所有视频帧都能通过,但延迟开始增加,在运行几秒钟后增加了几秒钟的延迟。

我有什么明显的遗漏吗?我似乎无法弄清楚这一点。

【问题讨论】:

  • 您确定是“Java”而不是您的网络吗?此外,与 TCP 不同,UDP 不保证数据包的传递、顺序或重复。
  • 使用 TCP 会导致延迟这一事实告诉我,您正试图向网络中注入比它可以承载的更多的数据。由于您有来自服务器端发送数据包的日志,因此您应该能够大致了解每秒发送了多少数据。是否与您的网络容量兼容?

标签: java c networking udp datagram


【解决方案1】:

获取一个像Wireshark 这样的网络工具,这样你就可以看到网络上发生了什么。

UDP 不会尝试重新传输,因此如果数据包在某处丢失,则由程序来处理丢失。 TCP 将努力将所有数据包按顺序传递给程序,丢弃重复数据并自行请求丢失的数据包。如果您看到高延迟,我敢打赌您也会看到大量 TCP 数据包丢失,这将显示为来自服务器的重新传输。如果您没有看到 TCP 重新传输,可能是客户端处理数据的速度不够快,无法跟上。

【讨论】:

  • 我打开了 wireshark 并使用 TCP 转储了数据包。我没有看到任何丢弃的数据包。 RTT 到 ACK(到 ACK 的往返时间)约为 40 毫秒。我的 Java 客户端使用专用线程来解析字节。
  • 感谢您的帮助,已解决。这是java接收线程的问题(线程被阻塞了一小段时间,没有监听数据包)。您的 TCP 丢弃数据包监视器建议很好。
【解决方案2】:

任何基于 UDP 的应用程序协议都不可避免地容易受到数据包丢失、重新排序和(在某些情况下)重复的影响。 UDP 中的“U”可以代表“不可靠”,就像在不可靠数据报协议中一样。 (好吧,它确实代表“用户”……但它肯定是记住 UDP 特性的好方法。)

UDP 数据包丢失通常是因为您的流量超出了服务器和客户端之间的一个或多个“跃点”的缓冲容量。发生这种情况时,数据包会被丢弃......由于您使用的是 UDP,因此没有传输协议级别的通知表明这种情况正在发生。

如果您在应用程序中使用 UDP,则应用程序需要考虑 UDP 的不可靠特性,实现自己的机制来处理丢弃和乱序的数据包并进行自己的流量控制。 (不考虑这可能对已经超载的网络产生影响的应用程序就爆出 UDP 数据包是一个糟糕的网络公民。)

(在 TCP 的情况下,数据包也可能被丢弃,但 TCP 正在检测并重新发送丢弃的数据包,并且 TCP 流控制机制正在启动以降低数据传输速率。最终结果是“延迟”。)

编辑 - 根据 OP 的评论,他的问题的原因是客户端没有“监听”一段时间,导致数据包(可能)被客户端的操作系统丢弃。解决这个问题的方法是:

  1. 使用一个专用的 Java 线程只是读取数据包并将它们排队进行处理,并且

  2. 为套接字增加内核数据包队列的大小。

但即使您采取了这些措施,您仍然可能会丢弃数据包。例如,如果机器过载,应用程序可能无法获得足够频繁的执行时间片来读取所有数据包并将其排队,然后内核不得不丢弃它们。

EDIT 2 - 关于 UDP 是否容易出现重复存在一些争论。毫无疑问,UDP 没有天生的重复检测或预防功能。但是,作为互联网的 IP 数据包路由结构也不太可能自发复制数据包。因此,如果确实发生了重复,则很可能会发生,因为发送方已决定重新发送 UDP 数据包。因此,在我看来,虽然 UDP 容易出现重复问题,但它不会导致它们本身 ...除非操作系统协议堆栈或 IP 结构中存在错误。

【讨论】:

  • 如果两个数据包最终采用不同的路由到达客户端,那么您会不会得到重新排序的数据包(从客户端的角度来看)?
  • @Eric 是 UDP,不是 TCP。
  • 为什么不重复? AFAIK,重复确实是可能的。
  • 只有在发送方发送两个内容相同的数据包时才会出现重复(例如重新发送)。在这种情况下,数据包在概念上是不同的,即使接收方无法检测到这一点。 AFAIK,路由器不会重新发送给定的 UDP 数据包。
  • UDP != "Unreliable" 数据报协议(即使它不可靠) - 它是“用户”数据报协议 - faqs.org/rfcs/rfc768.html
【解决方案3】:

虽然 UDP 支持长达 65535 字节的数据包(包括 UDP 标头,它是 8 个字节 - 但请参阅注释 1),但您和目标之间的底层传输不支持 IP 数据包那么长。例如,以太网帧的最大大小为 1500 字节 - 考虑到 IP 和 UDP 报头的开销,这意味着任何数据有效载荷长度超过约 1450 的 UDP 数据包都可能被分割成多个 IP 数据报。

一个最大大小的 UDP 数据包将被分成至少 45 个单独的 IP 数据报 - 如果这些片段中的任何一个丢失,则整个 UDP 数据包都会丢失。如果你的底层丢包率为 1%,那么你的应用会看到大约 36% 的丢包率!

如果您希望看到更少的数据包丢失,请不要发送大数据包 - 将每个数据包中的数据限制在 1400 字节左右(或者甚至执行您自己的“路径 MTU 发现”来确定您可以安全发送的最大大小没有碎片)。


  1. 当然,UDP也受到IP的限制,IP数据报的最大长度为65535,包括IP头。 IP 标头的大小范围为 20 到 60 字节,因此在 UDP 数据包中可传输的最大应用数据量可能低至 65467。

【讨论】:

  • 那里有几个不准确之处,请参阅我的回答。
  • 很好的实用建议——即使对 UDP 标头的大小(8 个字节,来自内存)存在争议。对于大多数流应用程序,保持 UDP 数据报的大小足够小以适合以太网帧具有许多优点: 将帧丢失时的数据丢失降至最低;减少延迟,因为不需要等待整个数据报;并且 - 对于某些应用程序 - 可以通过关闭流的 UDP 校验和并依靠以太网错误检测来丢弃错误的帧/数据报来减少处理负载。 MPEG 通常以每个 UDP 数据报 7 x 188 字节的 TS 帧发送。
【解决方案4】:

问题可能与您的传输缓冲区在您的 UDPSocket 中被填满有关。一次只发送UDPSocket.getSendBufferSize() 指示的字节数。使用setSendBufferSize(int size) 增加此值。

如果使用#send() 发送一个 DatagramPacket 大于 SO_SNDBUF 的设置然后它是 如果数据包实现特定 被发送或丢弃。

【讨论】:

    【解决方案5】:

    IP 支持 数据包 最多 65535 字节,包括 20 字节的 IP 数据包标头。 UDP 支持最多 65507 字节的数据报,外加 20 字节的 IP 标头和 8 字节的 UDP 标头。但是网络 MTU 是实际限制,不要忘记它不仅包括这 28 个字节,还包括以太网帧头。未分段 UDP 的真正实际限制是 576 字节的最小 MTU 减去所有开销。

    【讨论】:

    • 以太网有帧,没有数据包。以太网 MTU 是 1500 字节的 payload,它定义了一个 IP 分片的最大大小;它不包括帧头和尾随。 576 是节点为了传输 IP 分片而必须支持的最小 MTU,它不是未分片 UDP 的真正实际限制。此限制必须通过路径 MTU 发现来确定。此外,可以发送大于 MTU 的数据报而不使其不切实际,直到数据报或丢包变得太大。
    猜你喜欢
    • 1970-01-01
    • 2014-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-02
    • 1970-01-01
    • 1970-01-01
    • 2015-12-02
    相关资源
    最近更新 更多