确保UDP数据包的顺序

12

我正在使用两台计算机和一个应用程序来发送和接收UDP数据包。该应用程序没有流量控制,也禁用了ICMP。经常出现以下情况:当我通过该应用程序将文件作为UDP数据包发送时,有两个数据包的顺序改变,从而导致数据包丢失。

我已经禁用了所有防火墙,并且两台计算机之间没有连接任何硬件交换机(它们直接相连)。

是否有方法可以确保Winsock和send()以相同的方式发送数据包?

或者是操作系统在处理数据包的时候会排序?

还是需要网络设备配置?


在点对点连接中,我希望数据包是有序的,这是我的嵌入式系统经验证明了100%正确的假设。你看到的问题可能是由于接收端缩放引起的吗? - user1464603
6个回答

15

UDP是一种轻量级协议,它的设计不处理诸如数据包排序之类的事情。如果您想要稳健的数据包传递和排序,TCP是更好的选择。

UDP通常用于网络应用程序中,其中数据包丢失可接受或优于TCP重新请求数据包时产生的延迟,因此UDP通常用于媒体流传输。

如果您只能使用UDP,则需要开发一种识别乱序数据包并对其进行重新排序的方法。


9

UDP不保证数据包按顺序到达(甚至不能保证数据包能够到达)。如果您需要更高的可靠性,最好使用TCP。或者,您可以在数据报中添加序列标记,并在另一端重新排列它们,但为什么要重复造轮子呢?


由于某种特殊原因,我只能使用UDP。偶尔出现的少量数据包丢失是可以接受的,只是其中三个文件中有一个会出现丢包情况。我只需要找到一种方法让发送方按正确顺序发送数据包,其他问题对我来说并不重要。谢谢。 - Davidallencoe
1
这不仅仅是发送方按正确顺序发送数据包的问题,更重要的是接收方将它们重新组装成正确的顺序。就像我说的那样,在某个适当的级别上添加序列标记。 - crazyscot
2
UDP确保数据包完整或者不完整(即数据包带有校验和),并添加端口号到原始IP。但它不能保证传输的可靠性或顺序;这正是TCP增加的功能(基本上通过一直发送数据包直到对方确认接受为止)。保证正确的按序传递足以模拟数据流(因此TCP是一个流式套接字,因为这是非常普遍所需的)。 - Donal Fellows

7
有没有办法确保Winsock和send()发送数据包的方式与它们到达的方式相同?

这被称为TCP。

或者尝试可靠的UDP协议,例如UDT。我猜您可能在一个小型嵌入式平台上,因此需要一种更紧凑的协议,如贝尔实验室的RUDP


0

试图创建自己的TCP包装器是没有意义的。我们喜欢UPD的速度,而这只会减慢事情的进展。如果您设计协议使每个UDP数据报独立于其他数据报,则可以克服您的问题。只要头数据包先到达,我们的数据包可以以任何顺序到达。头部指示了应该到达多少数据包。此帖子创建十年后,UPD已变得更加可靠。不要尝试。


0

没有流量控制(ICMP已禁用)。

您可以使用UDP实现自己的流量控制:

  • 发送一个或多个UDP数据包
  • 等待确认(作为从接收方到发送方的另一个UDP数据包发送)
  • 重复以上步骤

请参见滑动窗口协议以获取更多详细信息。

[这将是您发送的数据包中的序列号之外的内容。]


0
这个问题已经有12年了,现在回答它似乎有点浪费时间。即使我提出的建议已经被提出过了。我在2002年处理过这个问题,在一个使用UDP广播与网络上运行的其他实例通信的程序中。如果一个数据包丢失了,那也没什么大不了的。但是如果我必须发送一个大于1020字节的大数据包,我会将其分成多个数据包。每个数据包都包含一个描述它是第几个数据包的头部,以及一个告诉我它是一个更大的整体数据包的头部。因此,结构被创建,有效载荷只需简单地放入(正确的)位置缓冲区中,并从需要的总字节数中减去字节。一旦所有数据包到达,所需的字节总数达到零,我就知道所有数据包都已到达。一旦所有数据包到达,该数据包就被处理。如果另一个广告数据包进来了,那么一切正在积累的东西都被丢弃。这告诉我其中一个片段没有到达。但是,这不是关键数据;代码可以没有它而生存。但是,我在每个数据包中实现了一个AdvReplyType,以便如果它是关键数据包,我可以用ADVERTISE_INCOMPLETE_REQUEST_RETRY数据包类型回复发送者,整个过程可以重新开始。
整个系统是为局域网操作而设计的,在我所有的调试/测试中,我很少丢失数据包,但在更大的网络中,我经常会收到乱序的数据包...但我确实收到了。由于现在已经过去12年,UDP广播似乎被许多IT管理员所不赞成,UDP看起来不再是一个好的、可靠的系统。ChrisW提到了滑动窗口协议;这就是我构建的东西...没有滑动部分!更像是一个“固定窗口协议”。我只是在每个有效载荷数据包的头部浪费了几个字节,以告诉总共有多少字节在这个重叠的数据包中,这是哪个数据包,以及它属于哪个唯一的MsgID,这样我就不必获取最初的数据包来告诉我期望收到多少个数据包。当然,我没有实现RFC 1982,因为这对此来说似乎太过复杂了。只要我收到一个数据包,我就知道了总长度、唯一的消息ID以及这个数据包的编号,这使得malloc()一个足够大的缓冲区来容纳整个消息变得非常容易。然后,一点数学就可以告诉我这个数据包在消息中的确切位置。一旦消息缓冲区被填满...我就知道我收到了整个消息。如果收到一个不属于这个唯一消息ID的数据包,那么我们就知道这是失败的,并且我们可能永远也无法获得旧消息的其余部分。
唯一提到这个的原因,是我认为仍然有时机和地方可以使用这样的协议。在慢速或不稳定网络上,TCP 的开销实际上太大了;但在这种情况下,也有最大可能性和可能担心数据包丢失。所以,再次强调,“可靠性”不能成为要求,否则你就回到了 TCP。如果今天我必须编写这段代码,我可能只会实现一个多播系统,整个过程对我来说可能会轻松得多。也许吧。它已经过去了12年,我可能忘记了很大一部分的实现细节。
对不起,如果我惊动了沉睡的巨人,那不是我的意图。这个原始问题引起了我的兴趣,并让我想起了我曾经写过的这个世纪之交的Windows C++ 代码。所以,请尽量将负面评论减少 - 如果可能的话!(当然,正面的评论总是受欢迎的!)开玩笑,当然,伙计们。

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