【问题标题】:Multiple sendto() using UDP socket使用 UDP 套接字的多个 sendto()
【发布时间】:2011-02-09 08:57:12
【问题描述】:

我有一个使用 UDP 与同一程序的其他实例通信的网络软件。由于不同的原因,我必须在这里使用 UDP。

我最近在通过 UDP 发送大量数据时遇到了问题,我不得不实施一个分片系统来将我的消息分成小数据块。到目前为止,它运行良好,但是当我必须发送 很多 数据块时,我现在遇到了问题。

我有以下算法:

  1. 将消息拆分成小的数据块(大约 1500 字节)
  2. 遍历数据块列表并为每个块使用sendto() 发送它

但是,当我发送大量数据块时,接收方只能收到前 6 条消息。有时它会错过第六个而收到第七个。视情况而定。

无论如何,sendto() 总是表示成功。当我通过环回接口 (127.0.0.1) 测试我的软件时总是会发生这种情况,但从不在我的 LAN 网络上测试。

如果我在sendto() 之间添加类似std::cout << "test" << std::endl; 的内容,则接收到每一帧。

我知道 UDP 允许丢包,并且我的帧可能由于多种原因而丢失,我想这与我发送数据块的速率有关。

这里的正确方法是什么?

  • 实现一些确认机制(就像 TCP)似乎有点过头了。
  • sendto() 之间添加一些任意等待时间是丑陋的,并且可能会降低性能。
  • 增加(如果可能的话)接收方 UDP 内部缓冲区?我什至不知道这是否可能。
  • 还有别的吗?

我真的需要你的建议。

非常感谢。

根据要求提供其他信息

必须使用 UDP 的原因是因为我有几个限制:

  1. TCP 不能很好地在 NAT 遍历中工作(至少在没有特定配置的情况下)
  2. 某些消息可能丢失。有些人不能。
  3. 消息传递顺序无关紧要。

【问题讨论】:

  • TCP 的设计目的不是为了成为重量级的。它旨在满足一个明确的要求:可靠、有序的数据包传送。如果你想要同样的保证,那么就没有捷径,没有你可以实现的 TCP-light 来代替 TCP。无论您做什么,本质上都是 TCP,只会有更多错误。
  • @Marcelo 是的,我完全同意。这正是我想避免重新实现轮子的原因。
  • 你有一个特别糟糕的 NAT 路由器吗?发送周期性心跳可以解决大多数 NAT 问题。
  • 我不明白 TCP 不能很好地与 NAT 遍历一起工作。 UDP 更难跨 NAT 工作。
  • @nos 我的项目必须适用于几乎所有可能的配置,而不仅仅是我的。我已经有一个心跳系统来维持 UDP“连接”。

标签: c sockets udp portability sendto


【解决方案1】:

实施确认机制听起来正是您需要做的。这样,您可以确保一次“正在传输”的数据包不超过 N 个,并且您可以重新传输长时间未确认的数据包。

【讨论】:

  • 感谢您的回答。你对我应该使用的算法有什么建议吗?我想为每个收到的块发送一个确认是次优的。
  • 正如其他人所说,这实际上取决于您的具体要求。为每个收到的数据包发送一个确认可能会很好(确认会很小),至少作为第一次切割。
  • 在发送下一个数据包之前要求每个数据包 1 个确认可能是非常次最佳。假设 RTT 是 25 毫秒。然后,您可以每 25 毫秒仅发送 1 条消息,如果您的数据包最大大小为 1500,则为 60kb/秒 - 如果您需要速度,则不是很快。
  • 我最终实现了每个数据包的确认机制。它工作正常,并且没有任何速度损失。 (我不需要遵守交付顺序,因此我可以异步发送消息/确认,从而避免@nos 建议的问题)。
【解决方案2】:

TCP 的存在正是为了解决这类问题。为什么 TCP 不是一个选项?您将不得不解决所有相同的问题并最终得到相同的解决方案,只是没有受益于 TCP 堆栈数十年的研究、开发和调试。

如果你真的必须使用 UDP,首先要问的问题是,关于 TCP 的保证,你愿意放弃什么?您是否乐于收到乱序的数据包?丢失一定百分比的数据包可以吗?你能处理重复数据包的到来吗?这些问题的答案有望带来设计。

在不了解您的具体情况的情况下,不可能用简单的“执行此操作,您会没事的”来回答您的问题,当然,除了“执行 TCP,您会没事的”。

【讨论】:

  • 感谢您的回答。我在问题末尾添加了更多信息。
【解决方案3】:

您应该实现确认和重传。要求例如每 N 个数据包确认一次,并将 N 个数据包保留在重传缓冲区中。

(也许你可以从rudp得到一些想法,或者通过UDP实现Il

UDP 不可靠,也没有提供流量控制。简短的故事是,你会不时丢失数据包 - 特别是如果你快速发送数据 - 如果没有足够的空间,内核或中间的任何路由器/交换机都会丢弃数据包 - 你可以使用一点魔法来避免这种情况发生。

【讨论】:

    【解决方案4】:

    UDP传输数据报,不可靠。

    TCP传输数据流,可靠。

    您想要的似乎是基于数据报的,但可靠。所以你需要在 UDP 或 TCP 上构建一些东西来给你。我确信在 UDP 上构建了完整的协议规范来提供这一点。您只需要找到并实施它们。

    【讨论】:

      【解决方案5】:

      如果您在仅发送 6 或 7 个数据包后通过环回接口丢失数据包,那么听起来您的接收缓冲区可能太小了。您可以使用 SO_RCVBUF 选项增加setsockopt 的大小。但是,如果您发送 1500 字节,那么如果这确实是问题所在,则意味着接收缓冲区只有大约 9K(或更可能是 8K,但这似乎是一个相当小的默认值)。我相信在 Windows 上默认接收缓冲区是 16K。

      即使假设增加接收缓冲区有帮助,您仍然必须解决其他人提到的问题。需要考虑的其他几件事可能是尝试动态确定the max packet size 以避免碎片。此外,手动配置在 ack 之间发送的数据包大小和数据包数量可能是有意义的。

      【讨论】:

      • +1 因为你是唯一一个谈到 SO_RCVBUF 选项的人。
      【解决方案6】:

      称 UDP 不可靠是一种简化,它试图将 TCP 视为所有网络疾病的灵丹妙药。同样,将 TCP 定义为可靠也是错误的。虽然 TCP 确实具有尝试确保数据传输的机制,但许多导致 UDP 数据包无法到达的故障也会导致 TCP 失败。

      例如,硬件网络故障将对 UDP 和 TCP 数据包产生相同的影响。如果故障仍然存在,那么 TCP 将不会像 UDP 那样可靠地通过。事实上,在这种情况下,TCP 的缺点在于它会尝试更长时间地完成丢失的原因。现在,如果您通过 Internet 发送数据,TCP 具有一些优势,因为无法预定义发送数据包的路由。然而,对于通过 LAN 发送数据,UDP 就足够了。如果您的数据包没有到达目的地,则表明存在需要纠正的硬件故障。 TCP 在这里无济于事。

      此外,在选择协议时,您还必须了解您的数据。如果您的数据是瞬态的,例如来自传感器的读数,则使用 UDP over TCP 会更有意义。如果在这种情况下丢失了一个数据包,那么它的影响很小,因为另一个数据包很快就会出现。另一方面,TCP 将退出并重试。到数据到达时,它已经过时了。

      事实上,TCP 是为流数据设计的。在这种情况下,重要的是所有数据包都可靠且有序地到达。 UDP 用于分组数据,并且用于这种类型的数据 UDP 是完全可以接受的,因为它可靠,开销更少,并且可以更快地检测到网络故障并从网络故障中恢复。

      【讨论】:

        【解决方案7】:

        无论如何,sendto() 总是表示成功。当我通过环回接口 (127.0.0.1) 测试我的软件时,总是会发生这种情况,但从不在我的 LAN 网络上测试。

        如果我添加类似 std::cout

        听起来你的接收器缓冲区太小了。

        一些建议:

        1. 增加接收缓冲区。 setsockopt SO_RCVBUF
        2. 让应用决定是否重传丢失的包。
        3. 确保负载适合 MTU,即负载 + HEADER

        【讨论】:

          猜你喜欢
          • 2015-07-27
          • 2011-04-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-06-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多