结论:UDP接收缓冲区上限由net.core.rmem_max控制,未同步调大该值时,setsockopt(SO_RCVBUF)设置超限部分会被内核静默截断,导致RcvbufErrors丢包;必须先用sudo sysctl -w net.core.rmem_max=33554432等命令提升上限,并结合带宽×RTT估算合理值(如16MB),再通过ss -nump验证rb值生效。

直接说结论:系统级 UDP 缓冲区上限由 net.core.rmem_max 和 net.core.wmem_max 控制,不改这两个值,应用层调 setsockopt(..., SO_RCVBUF, ...) 也白搭——超过上限的部分会被内核静默截断。
为什么改了 SO_RCVBUF 还是丢包?
这是最常踩的坑。你代码里写了:
int size = 32 * 1024 * 1024; // 32MB setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
但 net.core.rmem_max 当前是 2097152(2MB),那实际生效的接收缓冲区最多只有 2MB(内核还会翻倍,约 4MB 物理内存占用)。超出部分被忽略,netstat -su 里会持续出现 RcvbufErrors。
- 必须先查当前值:
sysctl net.core.rmem_max - 必须用 root 权限改:
sudo sysctl -w net.core.rmem_max=33554432(32MB) - 注意:该值影响所有新创建的 socket,包括 TCP;UDP 没有独立的
udp_rmem_max参数
流媒体场景下怎么设才合理?
不是越大越好。缓冲区过大会拖慢数据流转,增加端到端延迟(尤其对实时音视频),还可能挤占其他进程内存。
- 估算下限:按公式
Buffer ≥ 带宽 × RTT。例如 100Mbps 码流 + 50ms RTT → 至少需要 ~625KB,建议设为 4–8MB 起步 - 典型流媒体配置(千兆网卡、低延迟要求):
net.core.rmem_max = 16777216(16MB)net.core.wmem_max = 8388608(8MB)net.core.netdev_max_backlog = 5000(防突发包队列溢出) - 发送缓冲区通常比接收小,因为流媒体服务器发包节奏可控,而客户端收包易受网络抖动影响
如何验证改没生效?
别只信 sysctl 输出,要看到真实 socket 级别的使用情况:
- 用
ss -nump | grep udp查看某 UDP socket 的实时状态,字段如skmem:(r0,rb16777216,t0,tb8388608)中的rb和tb就是当前生效的接收/发送上限 - 跑压力测试时监控:
netstat -su | grep -E "(RcvbufErrors|SndbufErrors)"—— 这两个计数器归零才算真正压住 - 注意:
rmem_default和wmem_default只影响未显式调setsockopt的 socket,流媒体服务务必自己设,别依赖默认值
最后提醒一句:容器环境(Docker/Podman)里改 /proc/sys/net/core/rmem_max 默认只作用于当前命名空间,宿主机不受影响;但如果你在容器里跑流媒体服务,又没加 --sysctl 参数,那改了也是改了个寂寞。


















