如何在Linux上通过编程方式增加UDP套接字的每个套接字缓冲区?

3

我正在尝试理解在Linux上为我们的流媒体网络应用程序增加套接字缓冲区大小的正确方法。该应用程序在许多UDP套接字上接收可变比特率数据流。数据量在流的开始时显着增加,我已经使用了:

# sar -n UDP 1 200

为了显示UDP协议栈正在丢弃数据包

# ss -un -pa

为了显示每个套接字的Recv-Q长度在包被丢弃之前增长到接近极限(124928,从sysctl net.core.rmem_default),这意味着应用程序无法跟上流的开始。在丢弃足够多的初始数据包后,数据传输速率会减慢,应用程序赶上了。Recv-Q趋向于0并保持在那里一段时间。
我可以通过大幅增加rmem_default值来解决数据包丢失问题,这将增加套接字缓冲区大小,并给应用程序恢复大量初始突发时间。我的理解是,这会更改系统上所有套接字的默认分配。我宁愿只增加特定UDP套接字的分配,而不修改全局默认值。
我的初始策略是修改rmem_max并在每个单独的套接字上使用setsockopt(SO_RCVBUF)。然而,这个question让我担心会禁用所有套接字(而不仅仅是UDP)的Linux自动调整。

udp(7)描述了udp_mem设置,但我不确定这些值如何与rmem_default和rmem_max值交互。它使用的语言是“所有套接字”,因此我的怀疑是这些设置适用于完整的UDP堆栈,而不是单个UDP套接字。

udp_rmem_min是我在寻找的设置吗?它似乎适用于单个套接字,但全局适用于系统上的所有UDP套接字。

有没有一种安全地增加特定UDP端口使用的套接字缓冲区长度而不修改任何全局设置的方法?

谢谢。


如果这是Windows,我会说“尽快转移到重叠IO,最好是IOCP,以便运行堆栈的内核线程可以直接将数据接收到您提供的用户缓冲区中。创建一个100个64K缓冲区的池,并保持一些重叠读取排队。” 在Linux上肯定也有类似的东西吧?NIO/asio? - Martin James
你可以考虑将UDP接收循环移动到一个单独的高优先级线程中,该线程除了接收数据包之外什么都不做,然后将数据包交给主线程(或其他线程)进行实际处理。根据我的经验,当应用程序无法跟上传入的UDP数据时,通常不是因为CPU总体上不够快,而是因为接收线程偶尔忙于执行其他任务,没有及时调用recv()函数。通过将一个线程专门用于执行recv()操作和少量其他任务,可以最大程度地减少出现这种延迟的可能性。 - Jeremy Friesner
Jeremy - 我不确定那会如何有所帮助。即使recv()是系统上运行的最高优先级(也是唯一)线程,如果处理数据包所需的时间大于到达速率,则数据仍然可能填满队列。显然,应用程序无法维持该数据速率,但由于它仅在开始时发生,因此我想增加缓冲区深度以容纳它。 - bfallik-bamboom
1个回答

0

Jim Gettys已经武装起来并且来找你了。不要睡觉。

网络数据包洪流的解决方案几乎从来不是增加缓冲区。为什么你们的协议排队策略不退避呢?如果你试图以流的形式发送大量数据,为什么不能使用TCP(这就是TCP的设计目的)?


2
嘿。我不确定Bufferbloat的人们在这里是否有充分的理由。TCP不是一个选项,因为我们不愿意为了保证交付而交换它的增加开销。我们愿意丢失一些数据。但在这种情况下,我们正在失去大量数据,而且只是在流的开始阶段。一旦我们度过了最初的攻击,数据速率就会减少,我们能够使用非常小的缓冲深度维持操作。 - bfallik-bamboom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接