UDP文件传输项目 - 错误检查是否必要?

5
我被分配了使用UDP传输文件的经典任务。在不同的资源上,我读到了两种不同的说法:检查数据包的错误(在数据包中添加CRC)是必要的,并且UDP已经检查了损坏的数据包并将其丢弃,因此我只需要担心重新发送丢失的数据包。
那么哪一种正确呢?我需要手动对到达的数据包执行完整性检查吗?或者不正确的数据包已经被丢弃了?
顺便提一下,该项目使用的语言是Java。
编辑:一些来源(课本、互联网)表示校验和仅覆盖标题,从而确保发送方和接收方的IP地址是正确的等等。有些来源表示校验和也涵盖数据段。有些来源表示校验和可能涵盖数据段,但这是可选的,由操作系统决定。
编辑2:我问了我的教授,他们说IPv4中数据段的UDP错误检查是可选的,默认情况下IPv6中有。但我仍然不知道它是否在程序员的控制下、操作系统的控制下或其他层次的控制下……
4个回答

4

第一个事实:

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

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

总之,这意味着,UDP内置的校验和可能可靠或不可靠,这取决于您的环境。

第二个事实:

在传输过程中,与数据损坏相比,数据包丢失重排的威胁更加现实:UDP不保证

  • 所有数据包最终都会到达
  • 数据包按照发送顺序到达

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

结论:

仅通过追加接收到的数据包而不采取其他措施,必然会产生接收流与发送流不同的结果,除非处于非常有利的环境中,使其成为直接文件传输的次优协议。

如果您确实想要或必须使用UDP传输文件,则需要将TCP所固有的但不包含在UDP中的那些部分构建到应用程序中。虽然有一句话说,这很可能会导致对TCP的劣质重新实现。

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

实施建议:

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

在发送端:

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

在接收端:

  • 接收到的数据包会根据其校验和进行检查,
  • 如果所有序列号的副本都一致,那么已损坏的数据包会被负面确认,否则将被丢弃。
  • OK数据包的状态数组标记为“已接收/待处理”并带有时间戳。
  • 确认通过发送确认数据包来实现,如果已接收足够的块以填充确认数据包,或者最旧的“已接收/待处理”的时间戳变得太老(几毫秒到几百毫秒),则会发送确认数据包。
  • 确认数据包需要进行校验和,但不需要排序。
  • 已发送确认的块在状态数组中标记为“确认/待处理”并带有时间戳。

在发送方面:

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

在接收方面:

  • 接收到第i+N个块(N为窗口宽度)会将第i个块标记为“确认/已完成”,并向上滑动接收窗口。如果不是所有从接收窗口滑出的块都被标记为“确认/待处理”,则这构成了一个无法恢复的错误。
  • 对于状态为“确认/待处理”的块,时间戳上的超时将触发对该块的重新确认,因为原始确认消息可能已经丢失。

显然,如果发送窗口滑出文件结尾,则需要来自发送方的特殊消息类型以信号收到确认而不发送块N + i,我们通过简单地发送比实际存在更多的N块而没有有效负载来实现它。


1
这就是我说的话: ...使它成为直接文件传输不太优秀的协议这很可能导致 TCP 的低效重新实现。我的实现建议始于如果你确实希望或必须使用 UDP,所以我完全支持你。1. 你应该使用 TCP,2. 只有在必须使用 UDP 时,然后... - Eugen Rieck
感谢您的详细回答。这种方法是所谓的“回退N ARQ”吗? - uylmz

2
您可以确信接收到的数据包与发送的数据包相同(即,如果您发送数据包A并接收到数据包A,则可以确信它们是相同的)。传输层对数据包进行的CRC检查可以确保这一点。然而,由于UDP没有可靠的传递保证,您需要确保已接收到所有发送的内容,并确保正确排序。
换句话说,如果按顺序发送了数据包A、B和C,您实际上可能只会收到A和B(或者一个都没有)。您可能会收到无序的C、B、A。因此,您的检查需要考虑到TCP提供的可靠传递方面(验证排序,确保所有数据都在那里,并通知服务器重新发送您未收到的任何内容),以满足您所需的程度。
选择UDP而不是TCP的原因是,对于某些应用程序,数据排序和数据完整性都无关紧要。例如,在流式传输AAC音频数据包时,单个音频帧非常小,可以安全地丢弃少量音频帧或播放无序音频帧,而不会对听觉体验产生重大影响。如果99.9%的数据包被正确接收和排序,您可以正常播放流媒体,没有人会注意到。这对于某些蜂窝/移动应用程序非常有效,您甚至不必担心重新发送丢失的帧(请注意,Shoutcast和其他一些服务器在某些情况下使用TCP进行流媒体传输[以便于内部元数据],但他们不必这样做)。
如果您需要确保所有数据都在那里并且正确排序,则应使用TCP,它将负责验证数据是否全部在那里,正确排序并在必要时重新发送。

没问题,我很高兴能够帮助。谢谢你的赏金! - par
我认为这个答案只是关于TCP和UDP的一般信息(可以在每本网络书籍或互联网上找到),而不是我问题的确切答案。我问的是UDP是否提供数据错误检查。 - uylmz
“数据”是什么意思?我的回答澄清了UDP协议在每个数据包上提供完整性检查,但仅此而已。有效载荷完整性检查意味着UDP不提供多个数据包的聚合数据。聚合数据完整性涉及正确排序接收到的数据包,并确保所有数据包都已到达给定的有效载荷。TCP可以做到这一点,UDP则不能。 - par
顺便说一下,你的问题中提到了你被赋予了“通过UDP传输文件的经典任务”。UDP并不适合文件传输。为此,你应该使用TCP,否则你只是在尝试重新实现TCP。 - par
通过“经典的UDP文件传输任务”这个短语,我指的是这是计算机科学专业学生非常普遍的学校项目。目的是重新发明TCP协议,可能会有一些不同。 - uylmz

2
UDP协议使用与TCP协议相同的策略来检查带有错误的数据包 - 在数据包头中使用16位校验和。
UDP数据包结构是众所周知的(就像TCP一样),因此如果没有加密,数据包可以很容易地被篡改。添加另一个校验和(例如CRC-32)也会使其更加健壮。如果目的是加密数据(手动或通过SSL通道),我不会费心添加另一个校验和。
请还要考虑到数据包可能会被发送两次。确保您相应地处理它。
您可以在维基百科上检查两个数据包的结构,两者都具有校验和: 您可以更详细地检查TCP数据包结构,以获取有关如何处理丢失数据包的提示。TCP协议使用“序列号”和“确认号”来实现此目的。
希望这可以帮助您,祝您好运。

1
UDP会丢弃不符合每个数据包内部校验和的数据包;CRC检查有助于在应用程序层确定,一旦负载似乎已经完整,接收到的数据实际上是完整的(没有丢失的数据包),并且与发送的内容匹配(没有中间人或其他攻击)。

澄清一下,在应用层进行 CRC 校验应该对有效负载进行,以确保数据完整且顺序正确,而不是对单个数据包进行校验。你不需要复制传输层所做的事情。 - par

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接