UDP SetWriteBuffer 和 SetReadBuffer 如何处理 OS 的缓冲区?

How does UDP SetWriteBuffer and SetReadBuffer the OS's buffers?

描述

我正忙着用 Go 写一个高频 UDP 服务器。我估计至少有 1000 packets/second 两种方式。

然而,随着我通过 UDP 套接字发送的数据量的增长,我最终 运行 出现以下错误:read udp 127.0.0.1:1541->127.0.0.1:9737: wsarecv: A message sent on a datagram socket was larger than the internal message buffer or some other network limit, or the buffer used to receive a datagram into was smaller than the datagram itself.

我最终只是增加了我正在读取和写入的缓冲区的大小,如下所示:

buffer := make([]byte, 64 * 1024 * 1024) // used to just be 1024
l, err := s.socketSim.Read(buffer)

这工作正常,我不再收到错误...但是我可以跨 net 包中的两个函数:

s.socketSim.SetWriteBuffer(64 * 1024 * 1024)
s.socketSim.SetReadBuffer(64 * 1024 * 1024)

我了解到这两个作用于operating system's transmit buffer

问题

我什至关心设置操作系统缓冲区大小吗?为什么?应用程序缓冲区的大小如何影响操作系统缓冲区的大小?它们是否应该始终相同以及它们变得有多大 should/can?

首先,您不仅拥有设备上每个接口的 MTU 大小以及您 send/recving 来自的任何目的地,而且中间的每个设备也有一个 MTU 大小。出于这个原因,正如其他人所提到的,您可能希望使用 MTU 普遍接受的内容,因为您可能无法控制数据路由中的每个设备。在 UDP 的情况下,MTU 实际上只是意味着数据报在分段之前可以有多大。

其次,您几乎肯定希望 SND/RCV 缓冲区大于 MTU。这些是内核缓冲区,当您还没有准备好接收数据时,它们会保留数据。更大的 UDP RCV 缓冲区意味着内核将在将它们丢入深渊之前为您缓冲更多数据包。也许您需要为每个数据包做一些重要的工作。根据比特率,您可能需要更大或更小的内核缓冲区。

最后,您使用的是 UDP。无法保证您会按顺序或根本不会收到数据包。您和对等方之间的任何路由器都可以出于任何原因决定丢弃数据包。由于您使用的是 UDP,因此您应该为丢弃的和无序的数据包做好准备。您可能还需要某种重传机制,这会使事情变得更加复杂。

或者,如果丢包是不可接受的,并且知道时间不确定,您可能会考虑使用 TCP。

如果您使用 linux,您可以在 /proc/sys/net 中查看当前缓冲区大小。通常内核会加倍你要求的。

此外,您可以通过观察 /proc/net/udp 中的丢包来调整缓冲区大小。如果您看到下降,您可能希望使您的 rcv 缓冲区更大,尤其是在数据突发且处理密集的情况下。如果您的数据以一致的速率传入并且您仍在丢弃数据包,那么您处理它们的速度不够快。