在 linux 中,如果我尽可能快地调用 send() 为什么会丢失 UDP 数据包?

In linux, why do I lose UDP packets if I call send() as fast as possible?

隐含的问题是:如果 Linux 在套接字的发送缓冲区已满时阻止 send() 调用,为什么会有任何丢失的数据包?

更多详情: 我用 C 编写了一个小实用程序,以尽快将 UDP 数据包发送到单播地址和端口。我每次发送一个 1450 字节的 UDP 负载,第一个字节是一个计数器,每个数据包递增 1。我 运行 它在带有 1Gb 网卡(=相当慢)的桌面 PC 上的 VirtualBox 中的 Fedora 20 上。

然后我编写了一个小实用程序来从给定端口读取 UDP 数据包,它根据自己的计数器检查数据包的计数器,如果它们不同则打印一条消息(即 1 个或多个数据包已丢失)。我 运行 它在带有 1Gb 以太网网卡(=超快)的 Fedora 20 双至强服务器上。它确实显示了很多丢失的数据包。

两台机器都在本地网络上。我不知道它们之间的确切跳数,但我认为它们之间的路由器不会超过 2 个。

我尝试过的事情:

请赐教!

  • 如果过载,您提到的两个路由器中的任何一个都可能丢包,
  • 并且在某些情况下(例如过载),接收 PC 也可能会丢弃或丢失数据包。

即使 send() 在发送缓冲区已满时阻塞(前提是您没有在套接字上设置 SOCK_NONBLOCK 以将其置于非阻塞模式),接收方仍然必须很快足以处理所有传入的数据包。如果接收方或任何中间系统比发送方慢,则使用 UDP 时数据包将丢失。请注意,slower 不仅适用于网络接口的速度,还适用于整个网络堆栈以及用户空间应用程序。

在您的情况下,接收方很可能正在接收所有数据包,但在用户空间中无法足够快地处理它们。您可以通过 tcpdumpwireshark.

记录和分析您的流量来检查这一点

如果您不想丢失数据包,请切换到 TCP。

对于 UDP,SO_SNDBUF 套接字选项仅限制您可以发送的数据报的大小。与 TCP 一样,没有显式限制发送套接字缓冲区。当然,在内核中将帧排队到网卡。

换句话说,send(2) 可能会在不返回错误的情况下丢弃您的数据报(查看手册页底部对 ENOBUFS 的描述)。

然后数据包可能会在路径上的几乎任何地方被丢弃:

  • 发送网卡没有可用的硬件资源来处理请求,帧被丢弃,
  • 中间路由设备没有可用缓冲区space或实施某种拥塞避免算法,丢弃数据包,
  • 接收网卡无法接受给定速率的以太网帧,一些帧被忽略。
  • reader 应用程序没有足够的套接字接收缓冲区 space 来适应流量高峰,内核丢弃数据报。

不过从你所说的来看,这听起来很可能是虚拟机无法以高速率发送数据包。使用 tcpdump(1)wireshark(1) 尽可能靠近源头嗅探电线,并检查您的序列号 - 它会告诉您是否是发件人应该受到指责。

正如上述发帖人之一所说,UDP 是一种简单的数据报协议,不保证传送。要么是因为本地机器,网络上的设备等。这就是为什么许多当前的开发人员会建议,如果您想要可靠性,则切换到 TCP 的原因。但是,如果您真的想坚持使用 UDP 协议并且有很多正当理由这样做,您将需要找到一个可以帮助您保证交付的库。寻找 SS7 项目,尤其是在使用 UDP 传输语音、数据和信令信息的电话 API 中。对于您的唯一用途应用程序,我可以建议使用 enet UDP 库。http://enet.bespin.org/