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

时间:2015-04-14 13:24:27

标签: c linux sockets networking udp

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

更多细节: 我在C中编写了一个小实用程序,以尽可能快地将UDP数据包发送到单播地址和端口。我每次发送一个1450字节的UDP有效载荷,第一个字节是一个计数器,每个数据包递增1。我在具有1Gb nic(=非常慢)的台式PC上的VirtualBox内部的Fedora 20上运行它。

然后我写了一个小实用程序来读取来自给定端口的UDP数据包,该端口检查数据包的计数器与其自己的计数器,并在它们不同时打印一条消息(即丢失了一个或多个数据包)。我在Fedora 20 bi-xeon服务器上运行它,它具有1Gb以太网网络(=超快速)。它确实显示了许多丢失的数据包。

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

我尝试的事情:

  • 在每个send()之后添加延迟。如果我设置1ms的延迟,则不再丢失数据包。延迟100us将开始丢失数据包。
  • 使用setsockopt()将接收套接字缓冲区大小增加到4MiB。这没有任何区别......

请赐教!

4 个答案:

答案 0 :(得分:7)

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

换句话说,send(2)可能会丢弃您的数据报而不会返回错误(请查看手册页底部的ENOBUFS说明。)

然后,数据包可能会在路径上的任何位置掉落:

  • 发送网卡没有免费的硬件资源来处理请求,帧被丢弃,
  • 中间路由设备没有可用的缓冲区空间或实现一些拥塞避免算法,丢弃数据包,
  • 接收网卡不能以给定的速率接受以太网帧,有些帧只是被忽略。
  • 读者应用程序没有足够的套接字接收缓冲区空间来容纳流量峰值,内核丢弃数据报。

从你所说的话来看,VM很可能无法以高速发送数据包。用尽可能靠近源的tcpdump(1)wireshark(1)嗅探电线,并检查您的序列号 - 这将告诉您是否应该责怪发件人。

答案 1 :(得分:2)

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

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

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

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

答案 2 :(得分:0)

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

答案 3 :(得分:0)

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