5

我被赋予了使用 UDP 传输文件的经典任务。在不同的资源上,我已经阅读过检查数据包上的错误(将 CRC 与数据一起添加到数据包)是必要的,并且 UDP 已经检查损坏的数据包并丢弃它们,所以我只需要担心重新发送丢弃的数据包。

其中哪一项是正确的?我是否需要手动对到达的数据包执行完整性检查,或者不正确的数据包已经被丢弃?

顺便说一下,该项目的语言是 Java。

编辑:一些消息来源(课程书籍、互联网)说校验和仅涵盖标题,因此确保发送者和接收者 IP 是正确的等。一些消息来源说校验和涵盖数据段。一些消息来源说校验和可能涵盖数据段,但它是可选的,由操作系统决定。

编辑2:问我的教授,他们说数据段的UDP错误检查在IPv4中是可选的,在IPv6中是默认的。但我仍然不知道它是否在程序员的控制,或操作系统,或另一层......

4

4 回答 4

4

第一个事实:

UDP 有一个 16 位校验和字段,从数据包头的第 40 位开始。这有(至少)两个弱点:

  • 校验和不是强制性的,所有设置为 0 的位都定义为“无校验和”
  • 它是严格意义上的 16 位校验,因此容易受到未检测到的损坏。

这意味着,UDP 的内置校验和可能足够可靠,也可能不够可靠,具体取决于您的环境。

第二个事实:

比传输过程中的数据损坏更现实的威胁是丢包重新排序:USP 不保证

  • 所有到(最终)的数据包都到达
  • 数据包以与发送相同的顺序到达

事实上,UDP 根本没有内置机制来处理大于单个数据包的有效负载,这是因为它不是为此而构建的。

结论:

在没有额外措施的情况下在接收到的数据包后附加数据包必然会产生与发送流不同的接收流,但在最有利的环境中除外。这使得它不是直接文件传输的最佳协议。

如果您确实想要或必须使用 UDP 来传输文件,您需要构建这些部分,它们是 TCP 不可或缺的部分,而不是 UDP 到应用程序中的一部分。不过有一种说法,这很可能会导致 TCP 的重新实现效果不佳。

成功的实现包括许多对等文件共享协议,其中保护连接中断和数据包丢失或重新排序无论如何都需要成为应用程序功能的一部分,以击败或减轻过滤器。

实施建议:

对我们有用的是分块窗口实现:有效负载被分成固定且方便长度的块,(我们使用 1023 字节)在发送端和接收端保存 N 个这样的块的状态数组。

在发送方:

  • 发起一个 UDP 消息,其中包含这样的块、其在流中的序列号(不止一次)以及校验和或哈希。
  • 状态数组将此块标记为“已发送/待处理”并带有时间戳
  • 发送停止,如果完整的状态数组(发送窗口)被消耗

在接收方:

  • 接收到的数据包根据它们的校验和进行检查,
  • 如果序列号的所有副本都同意,则损坏的数据包被否定确认,否则丢弃
  • OK 数据包在状态数组中标记为“已接收/待处理”并带有时间戳
  • 如果已接收到足够的块来填充 ack 数据包,或者最旧的“接收/待处理”的时间戳变得太旧(从几毫秒到大约 100 毫秒),则确认通过发送一个 ack 数据包来工作。
  • Ack 数据包需要校验和,但不需要排序。
  • 已发送 ack 的块在状态数组中被标记为“ack/pending”并带有时间戳

在发送方:

  • 接收并检查确认数据包,丢弃损坏的数据包
  • 收到 ack 的块在状态数组中被标记为“ack/done”
  • 如果状态数组中的第一个块被标记为“确认/完成”,则状态数组向上滑动,直到它的第一个块再次没有完成。
  • 这可能会释放一个或多个要发送的未发送块。
  • 对于处于“已发送/待处理”状态的块,时间戳超时会触发该块的新发送,因为原始块可能已丢失。

在接收方:

  • 接收块 i+N(N 是窗口宽度)将块 i 标记为 ack/done,向上滑动接收窗口。如果不是所有滑出接收窗口的块都被标记为“ack/pending”,这构成了一个不可恢复的错误。
  • 对于状态为“ack/pending”的块,时间戳超时会触发该块的新 ack,因为原始 ack 消息可能已丢失。

显然,发送端需要一个特殊的消息类型,如果发送窗口滑出文件的末尾,来表示接收到一个 ack 而不发送块 N+i,我们通过简单地发送 N 个多于的块来实现它存在,但没有有效载荷。

于 2013-04-02T18:13:32.323 回答
2

您可以确定您收到的数据包与发送的数据包相同(即,如果您发送数据包 A 和接收数据包 A,您可以确定它们是相同的)。对数据包的传输层 CRC 检查确保了这一点。但是,由于 UDP 不能保证交付,您需要确保收到所有发送的内容,并且需要确保正确订购。

换句话说,如果数据包 A、B 和 C 以该顺序发送,您实际上可能只收到 A 和 B(或没有)。你可能会让它们乱序,C、B、A。所以你的检查需要照顾 TCP 提供的保证交付方面(验证排序,确保所有数据都在那里,并通知服务器重新发送你没有的任何东西' t 接收)到您需要的任何程度

首选 UDP 而不是 TCP 的原因是,对于某些应用程序而言,数据排序和数据完整性都不重要。例如,当流式传输 AAC 音频数据包时,单个音频帧非常小,以至于可以安全地丢弃其中的一小部分或乱序播放,而不会在很大程度上破坏聆听体验。如果 99.9% 的数据包被正确接收和排序,你可以很好地播放流并且没有人会注意到。这适用于某些蜂窝/移动应用程序,您甚至不必担心重新发送丢失的帧(请注意,在某些情况下,Shoutcast 和其他一些服务器确实使用 TCP 进行流传输 [以促进带内元数据],但它们不不必)。

如果您需要确保所有数据都在那里并且正确排序,那么您应该使用 TCP,它将负责验证数据是否都在那里,正确排序,并在必要时重新发送。

于 2013-04-02T17:45:31.940 回答
2

UDP 协议使用与 TCP 协议相同的策略来检查带有错误的数据包 - 数据包标头中的 16 位校验和。

UDP 数据包结构是众所周知的(以及 TCP),因此如果不加密,数据包很容易被篡改,添加另一个校验和(例如 CRC-32)也会使其更加健壮。如果目的是加密数据(手动或通过 SSL 通道),我不会费心添加另一个校验和。

还请考虑一个数据包可以发送两次。确保你相应地处理。

You can check both packet structure on Wikipedia, both have checksums:

You can check the TCP packet structure with more detail to get tips on how to deal with dropped packets. TCP protocol uses a "Sequence Number" and "Acknowledgment Number" for that purpose.

I hope this helps, and good luck.

于 2013-04-02T18:21:24.990 回答
1

UDP 将丢弃不符合内部每个数据包校验和的数据包;CRC 检查有助于在应用层确定,一旦有效负载看起来完整,接收的内容是否实际完整(没有丢弃的数据包)并且与发送的内容匹配(没有中间人或其他攻击) .

于 2013-04-02T17:34:39.920 回答