Python 3 Socket发送数据包时OSError: [Errno 105]问题排查
问题成因
通用套接字缓冲区参数不适用AF_PACKET
你调整的net.core.rmem_default/net.core.wmem_default等参数是为TCP/UDP这类传输层套接字设计的,AF_PACKET类型的链路层原始套接字并不依赖这些参数,它的发送缓冲区由网卡的TX环形缓冲区(TX Ring Buffer)和内核网络设备待处理队列(net.core.netdev_max_backlog)控制。无限制发送导致缓冲区瞬间饱和
你的代码是无限循环调用s.send(),没有任何流量控制逻辑。在10G网卡这种高带宽设备上,用户态发送速度远超网卡实际能处理的速率,内核的发送队列和网卡TX缓冲区被瞬间填满,系统无法分配新的缓冲区空间,从而抛出错误。而树莓派400的网卡是千兆级,性能有限,发送速度达不到饱和缓冲区的阈值,因此未触发异常。AF_PACKET套接字默认缓冲区未调整
即使你调了全局的wmem_max,如果没有单独设置AF_PACKET套接字的SO_SNDBUF选项,它可能还是使用较小的默认缓冲区大小,无法应对高流量场景。
解决方法
1. 添加流量控制逻辑
避免无限制发送,根据网卡的实际处理能力动态调整发送速率。例如可以设置微秒级的发送间隔:
import time # 在循环发送中添加间隔,根据实际带宽调整 while 1: s.send(packet) time.sleep(0.00001) # 可根据实际测试结果修改延迟时长
2. 调整网卡TX环形缓冲区大小
不同网卡的参数配置方式不同,以Intel网卡为例,使用ethtool调整TX环形缓冲区:
# 查看当前TX缓冲区大小 ethtool -g eth1 # 设置TX缓冲区为16384(最大值需参考网卡支持范围) ethtool -G eth1 tx 16384
3. 调大内核网络设备待处理队列参数
修改net.core.netdev_max_backlog,增大内核中每个网络设备的待处理发送队列长度:
# 临时生效 sysctl -w net.core.netdev_max_backlog=32768 # 永久生效,添加到/etc/sysctl.conf echo "net.core.netdev_max_backlog=32768" >> /etc/sysctl.conf sysctl -p
4. 设置AF_PACKET套接字的发送缓冲区
在代码中单独设置SO_SNDBUF选项,确保使用足够大的缓冲区:
with socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(0x0800)) as s: s.bind(("eth1", socket.htons(0x0800))) # 设置128MB发送缓冲区(需确保net.core.wmem_max已设为≥128MB) s.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 134217728) # 后续代码...
5. 使用非阻塞套接字配合IO等待
将套接字设为非阻塞,当缓冲区满时等待可用空间再发送:
with socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(0x0800)) as s: s.bind(("eth1", socket.htons(0x0800))) s.setblocking(False) packet = header + payload while 1: try: s.send(packet) except BlockingIOError: # 缓冲区满,等待片刻后重试 time.sleep(0.000001)
内容的提问来源于stack exchange,提问作者anmomu92

