隐含的问题是:如果 Linux 在套接字的发送缓冲区已满时阻止 send()
调用,为什么会有任何丢失的数据包?
更多详情: 我用 C 编写了一个小实用程序,以尽快将 UDP 数据包发送到单播地址和端口。我每次发送一个 1450 字节的 UDP 负载,第一个字节是一个计数器,每个数据包递增 1。我在配备 1Gb 网卡(=相当慢)的台式机上的 VirtualBox 内的 Fedora 20 上运行它。
然后我编写了一个小实用程序来从给定端口读取 UDP 数据包,它根据自己的计数器检查数据包的计数器并打印一条消息,如果它们不同(即 1 个或多个数据包已丢失)。我在带有 1Gb 以太网网卡(=超快)的 Fedora 20 双至强服务器上运行它。它确实显示了许多丢失的数据包。
两台机器都在本地网络上。我不知道它们之间的确切跳数,但我不认为它们之间有超过 2 个路由器。
我尝试过的事情:
- 在每个
send()
之后添加一个延迟。如果我将延迟设置为 1 毫秒,则不会再有任何数据包丢失。 100us 的延迟将开始丢失数据包。 - 使用
setsockopt()
将接收套接字缓冲区大小增加到 4MiB。这没有任何区别...
请赐教!
最佳答案
对于 UDP,SO_SNDBUF
套接字选项仅限制您可以发送的数据报的大小。与 TCP 一样,没有显式限制发送套接字缓冲区。当然,网卡的帧在内核中排队。
换句话说,send(2)
可能会在不返回错误的情况下丢弃您的数据报(查看手册页底部的 ENOBUFS
的描述)。
然后数据包可能会在路径上的几乎任何地方被丢弃:
- 发送网卡没有可用的硬件资源来处理请求,帧被丢弃,
- 中间路由设备没有可用的缓冲空间或实现某种拥塞避免算法,丢弃数据包,
- 接收网卡不能接受给定速率的以太网帧,有些帧会被忽略。
- 阅读器应用程序没有足够的套接字接收缓冲区空间来适应流量高峰,内核丢弃数据报。
不过从你所说的来看,VM 无法以高速率发送数据包的可能性很大。使用 tcpdump(1)
或 wireshark(1)
尽可能靠近源嗅探线路,并检查您的序列号 - 它会告诉您是否是发件人是罪魁祸首。
关于c - 在 Linux 中,如果我尽可能快地调用 send() 为什么会丢失 UDP 数据包?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29628555/