我被赋予了使用UDP传输文件的经典任务。在不同的资源上,我已经阅读了检查数据包上的错误(在数据包中添加CRC)是必要的,而UDP已经检查了损坏的数据包并丢弃了它们,所以我只需要担心重新发送丢失的数据包。
其中哪一个是正确的?我是否需要手动对到达的数据包执行完整性检查,或者已经丢弃了不正确的数据包?
顺便说一下,这个项目的语言是Java。
编辑:一些来源(课程书、互联网)说校验和只覆盖报头,因此确保发送方和接收方的IP是正确的等等。一些来源说校验和也覆盖数据段。一些消息来源称校验和可能覆盖数据段,但这是可选的,由操作系统决定。
编辑2:问了我的教授,他们说UDP错误检查在IPv4中是可选的,在IPv6中是无效的。但我仍然不知道它是在程序员的控制下,还是在操作系统的控制之下,还是在另一层。。。
第一个事实:
UDP具有从数据包报头的比特40开始的16比特校验和字段。这至少有两个弱点:
- 校验和不是强制性的,所有设置为0的位都定义为"无校验和"
- 它是一个16位的检查-严格意义上的和,因此它很容易被未检测到的损坏
总之,这意味着UDP的内置校验和可能不够可靠,也可能不够可靠。
第二个事实:
比传输过程中的数据中断更现实的威胁是数据包丢失的重新排序:USP对没有任何保证
- 所有的数据包(最终)都会到达
- 数据包按照发送的相同顺序到达
事实上,UDP根本没有内置机制来处理比单个数据包更大的有效载荷,这源于它不是为这个目的而构建的。
结论:
在没有附加措施的情况下将接收到的一个又一个分组追加必然会在除了最有利的环境之外的所有环境中产生不同于发送流的接收流。,使其成为直接文件传输的不太理想的协议。
如果确实想要或必须使用UDP传输文件,则需要在应用程序中构建那些部分,这些部分是TCP的组成部分,但不是UDP的组成部分。不过,有一种说法是,这很可能会导致TCP的重新实现。
成功的实现包括许多对等文件共享协议,在这些协议中,防止连接中断、数据包丢失或重新排序需要成为应用程序功能的一部分,以击败或减轻过滤器的影响。
实施建议:
对我们有效的是分块窗口实现:有效载荷被分成固定且方便长度的块,(我们使用了1023个字节)在发送端和接收端保留了一个由N个这样的块组成的状态数组。
发送端:
- UDP消息被初始化,包含这样的块、流中的序列号(不止一次)以及校验和或散列
- 状态数组使用时间戳将此区块标记为"已发送/挂起"
- 如果使用了完整的状态数组(发送窗口),则发送停止
在接收端:
- 对照它们的校验和来检查接收到的分组
- 如果序列号的所有副本一致,损坏的数据包将被否定确认,否则将被丢弃
- OK数据包在状态数组中标记为"已接收/挂起",并带有时间戳
- 如果已经接收到足够的块来填充ack数据包,或者最旧的"接收/挂起"的时间戳太旧(几毫秒到100毫秒),则通过发送ack数据包来进行确认
- Ack数据包需要校验和,但不需要排序
- 已发送ack的块在状态数组中标记为"ack/挂起",并带有时间戳
发送端:
- 接收并检查Ack数据包,丢弃损坏的数据包
- 收到ack的块在状态数组中标记为"ack/done"
- 如果状态数组中的第一个区块被标记为"ack/done",则状态数组会向上滑动,直到其第一个区块再次未完成为止
- 这可能会释放一个或多个要发送的未发送块
- 对于状态为"已发送/挂起"的块,时间戳上的超时会触发此块的新发送,因为原始块可能已丢失
在接收端:
- 块i+N的接收(N是窗口宽度)将块i标记为ack/done,向上滑动接收窗口。如果不是所有从接收窗口滑出的块都被标记为"ack/pending",这就构成了不可恢复的错误
- 对于状态为"ack/pending"的区块,时间戳上的超时会触发该区块的新ack,因为原始ack消息可能已经丢失
显然,如果发送窗口从文件末尾滑出,发送端需要一种特殊的消息类型,以在不发送块N+i的情况下发出接收ack的信号,我们通过简单地发送比现有多的N个块来实现它,但没有有效载荷。
您可以确保收到的数据包与发送的数据包相同(即,如果发送数据包A和接收数据包A,则可以确保它们相同)。数据包上的传输层CRC检查确保了这一点。但是,由于UDP不能保证交付,您需要确保收到了发送的所有内容,并且您需要确保正确订购。
换言之,如果数据包A、B和C按该顺序发送,您实际上可能只收到A和B(或者没有收到)。你可能会让它们失灵,C,B,A。因此,你的检查需要注意TCP提供的保证交付方面(验证订购,确保所有数据都在那里,并通知服务器重新发送你没有收到的数据)到你需要的任何程度。
首选UDP而不是TCP的原因是,对于某些应用程序来说,数据排序和数据完整性都不重要。例如,当流式传输AAC音频包时,单个音频帧非常小,因此可以安全地丢弃或无序播放少量音频帧,而不会在很大程度上干扰收听体验。如果99.9%的数据包被正确接收和排序,你可以很好地播放流,没有人会注意到。这适用于一些蜂窝/移动应用程序,您甚至不必担心重新发送丢失的帧(请注意,Shoutcast和其他一些服务器在某些情况下确实使用TCP进行流传输[以方便带内元数据],但它们不必这样做)。
如果您需要确保所有数据都在那里并正确排序,那么您应该使用TCP,它将负责验证数据是否在那里,正确排序,并在必要时重新发送。
UDP协议使用与TCP协议相同的策略来检查有错误的数据包-数据包标头中的16位校验和。
UDP数据包结构是众所周知的(与TCP一样),因此如果不加密,数据包很容易被篡改,添加另一个校验和(例如CRC-32)也会使其更加健壮。如果目的是加密数据(手动或通过SSL通道),我不会麻烦添加另一个校验和。
还请考虑到一个数据包可以发送两次。确保你相应地处理这个问题。
你可以在维基百科上检查两个数据包结构,两者都有校验和:
- 传输控制协议
- 用户数据报协议
您可以更详细地检查TCP数据包结构,以获得如何处理丢弃数据包的提示。TCP协议为此使用"序列号"one_answers"确认号"。
我希望这能有所帮助,祝你好运。
UDP将丢弃不符合内部每个数据包校验和的数据包;CRC检查有助于在应用层确定,一旦有效载荷看起来是完整的,接收到的内容是否实际上是完整的(没有丢弃的数据包)并与发送的内容匹配(没有中间人或其他攻击)。