sockets - 在 UDP 套接字上即时更改 SOL_SOCKET、SO_RCVBUF 的可预测行为是什么?
问题描述
如果我们在 Linux 系统上动态调整 UDP 服务器套接字的输入缓冲区大小,会发生什么?
setsockopt(sock, SOL_SOCKET, SO_RCVBUF, ...)
我对这些问题特别感兴趣:
- 如果我缩小到缓冲区中的当前值以下,这会简单地丢弃最旧/最新的吗?数据报正确,还是可以刷新那里的所有内容,或者更糟糕的是它会破坏数据,例如截断数据报?
- 缩小缓冲区甚至会节省内存还是阻止内存被系统重用?
- 行为是可预测的还是有时会随机表现?
解决方案
首先也是最重要的:术语“缓冲区”可能会造成混淆:内核实际上并没有将数据包保存在固定大小的缓冲区中,而是保存在称为“积压”的队列中(请参阅 参考资料include/net/sock.h:400
)。大小设置通过SO_RCVBUF
并且SO_SNDBUF
仅限制积压的最大大小。
如果我缩小到缓冲区中的当前值以下,这会简单地丢弃最旧/最新的吗?
不,已经收到的会被保留。没有数据报被丢弃。当您这样做时,唯一发生的事情setsockopt(SO_RECVBUF)
是套接字字段的值发生了sk_rcvbuf
变化。不执行其他操作。
这样做的真正效果只有在接收到更多的数据包时才能看到:所有后续接收到的数据报都会立即被丢弃,并且会继续被丢弃,直到队列缩小到设定的大小以下(即用户空间接收到足够的数据报)。
缩小缓冲区甚至会节省内存还是阻止内存被系统重用?
正如我之前所说,由于“缓冲区”并不是真正的缓冲区并且没有固定大小,因此更改SO_RECVBUF
不会立即改变任何内容。
有两种情况:
- 如果 backlog 大小低于(或等于)指定大小:新的最大大小将受到限制,因此将来会以可能丢失数据包为代价来节省内存。
- 如果 backlog 大小超过指定大小:当用户空间接收到缓冲的数据包时,内存最终会被释放,并且不会再次增长超过设定值。这将在将来再次节省内存。
行为是可预测的还是有时会随机表现?
查看内核代码,我会说按照我上面描述的方式 100% 可预测。但是,我不完全确定这可能会记录在哪里。如果您将发送和接收“缓冲区”视为队列(实际上是),我会说这有点直观。
推荐阅读
- java - Thymeleaf 操作执行 concat 而不是 multiply
- ios - 是否可以在 Realm 迁移中重命名和更改属性的数据类型
- c# - c#静态类中的8个可为空的引用类型
- python - Argparser 返回“--mode=client --port=52085”并崩溃
- ruby-on-rails - 是否能够回滚 Stripe 更新
- python - 如何在keras中将以下顺序模型转换为函数模型
- hibernate - 无法使用 thymelea、jpa 注册新用户 Spring Boot 1.xx
- python - 为什么我的 Python Beautifulsoup 程序会同时出现属性错误,然后又会同时自我修复?
- sql - 计数函数而不是计数(*)
- c# - LINQ - 比较我的模型的两个列表,它们共享一个值不相等的属性