如何获取TCP/UDP Socket接收缓冲区容量及当前缓存数据大小?
TCP/UDP Socket接收缓冲区参数获取方法
获取接收缓冲区容量上限
你测试的getsockopt()本身就可以实现这个需求,不管TCP还是UDP Socket,只要传入SO_RCVBUF选项,就能拿到内核为该Socket分配的接收缓冲区的实际容量上限。注意部分系统会对应用设置的SO_RCVBUF值做翻倍处理(比如Linux会默认翻倍用来存储内核开销的skb结构体),所以拿到的值和你之前setsockopt设置的值不一致是正常现象。
示例代码片段:
int rcv_buf_size = 0; socklen_t opt_len = sizeof(rcv_buf_size); getsockopt(sock_fd, SOL_SOCKET, SO_RCVBUF, &rcv_buf_size, &opt_len); // 此时rcv_buf_size就是接收缓冲区的容量上限
获取缓冲区当前已缓存的待读数据大小
getsockopt()的SO_RCVBUF确实拿不到这个实时值,根据场景不同可以选下面的方法:
通用跨平台方案(TCP/UDP均可用)
用ioctl(类Unix系统)/ioctlsocket(Windows系统)搭配FIONREAD选项,不需要实际读取缓冲区数据,就能拿到当前可读的字节数,没有额外副作用,是最常用的方案:
int pending_size = 0; // Linux/macOS等类Unix系统调用 int ret = ioctl(sock_fd, FIONREAD, &pending_size); // Windows系统替换为下面的调用即可 // int ret = ioctlsocket(sock_fd, FIONREAD, &pending_size); if (ret < 0) { // 错误处理逻辑 }
注意协议差异:对TCP Socket来说,
pending_size就是接收缓冲区里所有待读取的连续TCP流字节总数;对UDP Socket来说,绝大多数系统(Linux、Windows、macOS)里pending_size返回的是接收队列中第一个待读UDP报文的长度,不是所有待读报文的总长度,足够用来判断下一次读操作需要准备多大的缓冲区避免报文截断。
Linux平台专属方案
如果是Linux环境,有更细分的选项可以用:
- 对TCP Socket:可以直接用
getsockopt()搭配TCP_IOCINQ选项,返回值和FIONREAD完全一致,都是待读的总字节数,如果TCP连接已经收到对端发的FIN包,返回值为0。 - 对UDP Socket:如果你确实需要拿到接收缓冲区里所有待读UDP报文的总大小(而不只是第一个报文的长度),可以读取
/proc/net/udp(IPv4)或/proc/net/udp6(IPv6)文件中对应Socket条目下的rx_queue字段,这个字段存储的就是该Socket接收队列的总缓存字节数。
不建议使用的方法
- 不要用带
MSG_PEEK标志的recv/recvfrom试探长度:这个操作虽然不会把数据从缓冲区移除,但会有副作用,比如会移动TCP的紧急指针位置,UDP场景下如果传入的缓冲区长度小于报文实际长度,还是会出现报文截断问题,性能也比ioctl差。 - 不要在用户态自己维护计数:内核会处理TCP重传、丢包、UDP校验错误丢包、缓冲区自动调整等逻辑,用户态统计的已接收字节数和内核实际缓存的数据量一定会有偏差,结果不准。
内容的提问来源于stack exchange,提问作者lysS
相关产品推荐
相关产品推荐

