ss输出里的rcv_space是当前socket实际生效的接收缓冲区字节数,即内核最终分配的可用空间,由setsockopt设置后经内核翻倍、页对齐及rmem_max截断所得,不显示发送缓冲区。

直接看运行中的 UDP/TCP socket 实际缓冲区大小,用 ss -ulnep 或 ss -tlnep 最快;想查系统级默认/上限值,得翻 /proc/sys/net/core/ 下的文件。
ss 命令显示的是哪个缓冲区?
ss 输出里的 rcv_space 是当前 socket 实际生效的接收缓冲区字节数(即内核为该 socket 分配的可用空间),但不显示发送缓冲区。这个值是应用调用 setsockopt(..., SO_RCVBUF, ...) 后,内核最终确认的大小 —— 它可能被内核自动翻倍(用于管理开销),也可能被截断到 net.core.rmem_max 以下。
- UDP socket 示例输出:
UNCONN 0 0 *:5353 *:* users:(("avahi-daemon",pid=1234,fd=12)) ino:12345 sk:12345<->rcv_space:131072→ 实际接收缓冲区为 128KB - TCP 监听 socket 也会显示
rcv_space,但 ESTABLISHED 连接的缓冲区可能动态变化,ss不保证实时反映最新值 - 如果看不到
rcv_space,说明没加-e参数,或权限不足(需 root 才能显示-p和部分-e字段)
为什么 getsockopt 返回的 SO_RCVBUF 值和 ss 不一样?
因为 getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &val, &len) 返回的是你“设置过的值”,而 ss 显示的是内核“实际分配的值”。内核会把传入的 SO_RCVBUF 值至少翻一倍(预留管理空间),再向上取整到页对齐,最后受限于 net.core.rmem_max。
- 比如你
setsockopt(..., SO_RCVBUF, 64*1024),getsockopt可能返回 65536,但ss显示rcv_space:131072 - 若你设了 2MB,但
net.core.rmem_max是 1MB,则ss显示 1MB,getsockopt仍返回你传的 2MB(内核悄悄截断了) - 所以验证是否生效,必须看
ss或再次getsockopt—— 且后者要确保optlen初始化正确,否则可能读到垃圾值
/proc/sys/net/core/ 下哪些文件真正管 UDP 缓冲区?
UDP 不区分协议栈层级,它复用通用 socket 缓冲区参数:net.core.rmem_default、net.core.rmem_max、net.core.wmem_default、net.core.wmem_max。TCP 还额外有 net.ipv4.tcp_rmem 等,但 UDP 完全不读它们。
- 新建 UDP socket 时,默认使用
net.core.rmem_default和net.core.wmem_default - 调用
setsockopt(..., SO_RCVBUF, ...)时,上限由net.core.rmem_max决定,不是net.ipv4.tcp_rmem的第三个值 - 修改后需用
sysctl -w net.core.rmem_max=...持久化(写入/etc/sysctl.conf),否则重启失效
用 getsockopt 查缓冲区大小时最容易漏掉什么?
三个硬伤:没初始化 optlen、没检查返回值、没意识到 UDP socket 必须先 bind() 或 connect() 才能拿到有效值。
-
socklen_t optlen = sizeof(int);必须显式赋值,不能只声明;否则getsockopt可能写越界或读不到数据 - 即使
setsockopt成功,也要再调一次getsockopt确认最终值 —— 因为内核可能调整 - 对未绑定的 UDP socket 调
getsockopt(SO_RCVBUF)通常能返回默认值,但某些内核版本会失败;稳妥做法是先bind()到INADDR_ANY端口再查
真正决定缓冲区上限的不是代码里写的数字,而是 net.core.rmem_max 这个开关;改完内核参数后,老进程不会自动继承新值,必须重启或重新创建 socket。


















