为何TCP套接字SO_SNDBUF默认值为wmem_default的1/13且最小值异常?
问题背景与现象
我在做套接字练习时写了这段代码:
const int socketfd = socket(AF_INET, SOCK_STREAM, 0); int tcprcvbuf; socklen_t tcprcvbuflen = sizeof(tcprcvbuf); getsockopt(socketfd, SOL_SOCKET, SO_SNDBUF, &tcprcvbuf, &tcprcvbuflen); printf("Default send buffer (TCP): %d\n", tcprcvbuf);
输出结果为:
Default send buffer (TCP): 16384
但执行命令cat /proc/sys/net/core/wmem_default得到的结果是212992,是前者的13倍。
另外,根据socket(7)手册的描述:
SO_SNDBUF
设置或获取套接字最大发送缓冲区字节数。使用setsockopt(2)设置时,内核会将该值翻倍(预留记账开销空间),getsockopt(2)会返回翻倍后的值。默认值由/proc/sys/net/core/wmem_default文件设置,最大值由/proc/sys/net/core/wmem_max文件设置。该选项的最小(翻倍后)值为2048
我测试setsockopt时发现:设置任何小于2304的值,getsockopt都会返回4608(符合翻倍逻辑);设置大于2304的值则返回预期的翻倍值。但手册明确说最小翻倍后的值是2048,这明显矛盾,请问我忽略了什么要点?
问题解答
关于默认send buffer与wmem_default不匹配的问题
这是因为TCP套接字的默认缓冲区大小会被TCP层额外调整,不会直接沿用net/core/wmem_default的全局值。
全局的wmem_default是所有套接字类型(包括UDP、RAW等)的基础默认值,但TCP有独立的缓冲区初始化逻辑:内核会根据系统内存大小、TCP拥塞控制策略等,给新建的TCP套接字设置更保守的初始缓冲区(比如你看到的16384),而非直接使用全局wmem_default。只有当你显式调用setsockopt设置SO_SNDBUF时,才会以wmem_default和wmem_max作为约束边界。
关于最小缓冲区不符合手册描述的问题
手册里的“最小(翻倍后)值为2048”是套接字层的内核最低下限,但实际还有TCP层的额外限制:TCP要求每个套接字的发送缓冲区至少能容纳一个完整的TCP分段(segment)。
在多数系统中,TCP最小分段大小加上TCP头部、IP头部的开销,换算后对应的缓冲区需求刚好是4608(即你看到的2304翻倍后的值)。所以当你设置的SO_SNDBUF值过小,导致翻倍后低于TCP要求的最小值时,内核会自动将缓冲区提升到TCP允许的最小尺寸4608——这相当于TCP层的要求覆盖了套接字层的2048下限,实际生效的是两者中的较大值。
内容的提问来源于stack exchange,提问作者Lady Idiot

