udp文件传输项目 - 是否需要进行错误检查?

uyl*_*lmz 5 java udp protocols file transfer

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

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

顺便说一句,该项目的语言是Java.

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

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

Eug*_*eck 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”
  • 如果状态数组中的第一个块被标记为“ack/done”,则状态数组向上滑动,直到其第一个块再次未完成。
  • 这可能会释放一个或多个要发送的未发送块。
  • 对于状态为“已发送/待处理”的块,时间戳超时会触发该块的新发送,因为原始块可能已丢失。

在接收端:

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

显然,发送端需要一种特殊的消息类型,如果发送窗口滑出文件末尾,则在不发送块 N+i 的情况下发出确认信号的接收信号,我们通过简单地发送 N+i 个块来实现它存在,但没有有效负载。