在 Windows 上使用 Docker Desktop 和 Clash Verge 时,我遇到了一个比较奇怪的网络问题:部分网页开始间歇性无法打开,Clash Verge 日志中频繁出现 TCP Socket 创建失败的错误。
最初以为是代理节点、TUN 模式或 DNS 配置出了问题,但经过逐步排查,最终发现异常与 Docker Desktop 的网络连接有关。而真正让问题恢复正常的操作,并不是修改 Docker 或 Clash Verge 配置,而是升级 WSL。
这篇文章记录完整的排查过程、关键命令和最终解决办法,希望能为遇到类似问题的 Windows 开发者提供参考。
一、问题现象
故障发生时,Clash Verge 日志中出现以下错误:
error: failed to create session:
dial tcp 160.191.40.144:37274:
bind: An operation on a socket could not be performed
because the system lacked sufficient buffer space
or because a queue was full.
与此同时,浏览器出现间歇性网络故障:有些网页可以正常打开,有些则连接失败,而且并不固定发生在某个网站。
错误中的关键信息是:
bind: An operation on a socket could not be performed
because the system lacked sufficient buffer space
or because a queue was full.
这是 Windows Winsock 的 WSAENOBUFS (10055) 错误,表示系统无法为 Socket 操作分配所需的资源。
它不一定代表物理内存不足,还可能与 TCP 临时端口耗尽、网络缓冲区不足或其他网络资源限制有关。
因此,虽然错误出现在 Clash Verge 中,但不代表 Clash Verge 就是故障来源。
二、排查环境
出现问题时的主要环境如下:
| 组件 | 版本或配置 |
|---|---|
| 操作系统 | Windows 10.0.26300.9457 |
| Docker Desktop | 4.94.0 (241994) |
| Docker Engine | 29.8.2 |
| WSL | 2.3.26.0 |
| Linux Kernel | 5.15.167.4-1 |
| WSL 发行版 | Ubuntu 22.04 |
| Traefik | v3.6 |
| 代理软件 | Clash Verge(Mihomo 内核) |
| Docker 后端 | WSL 2 |
Docker Desktop 中运行了约 10 个容器,包括 Laravel/Swoole、Traefik、MySQL、Redis 以及其他开发服务。
本地域名使用 .test,通过 Traefik 反向代理访问各个容器服务。
三、发现 Windows TCP 端口资源异常
1. 查看 TCP 连接数量
首先在管理员 PowerShell 中执行:
(Get-NetTCPConnection).Count
得到:
1889
接着统计各 TCP 状态:
Get-NetTCPConnection | Group-Object State | Sort-Object Count -Descending | Select-Object Name, Count
结果:
| 状态 | 数量 |
|---|---|
| Bound | 1550 |
| Established | 161 |
| Listen | 64 |
| TimeWait | 62 |
| SynSent | 12 |
| CloseWait | 3 |
| FinWait2 | 2 |
其中最引人注意的是 Bound,数量达到 1550。
Bound 表示 Socket 已绑定本地地址或端口,但并不等于已经建立的 TCP 连接,也不能仅凭数量判断是否存在资源泄漏。
不过,结合系统出现的 Socket 创建失败错误,这个异常高的数量值得进一步调查。
2. 找出占用最多 Socket 的进程
执行:
Get-NetTCPConnection | Group-Object OwningProcess | Sort-Object Count -Descending | Select-Object -First 15 Name, Count
随后结合进程 ID 查询对应程序。
结果发现:
| 进程 | TCP 记录数量 |
|---|---|
| com.docker.backend | 1406 |
| verge-mihomo | 175 |
| 其他进程 | 相对较少 |
原来,大量 TCP Socket 都归属于 Docker Desktop 的后台进程:
C:\Program Files\Docker\Docker\resources\com.docker.backend.exe
这时,排查重点开始从 Clash Verge 转向 Docker Desktop。
四、发现动态端口范围被限制
继续查看 Windows TCP 动态端口范围:
netsh int ipv4 show dynamicport tcp
得到:
协议 tcp 动态端口范围
启动端口 : 49152
端口数 : 1638
这意味着当前配置的动态端口范围只有:
49152–50789
而 Windows 常见的默认动态端口范围是:
49152–65535
总共 16384 个端口。
当前端口池只有常见默认值的十分之一左右。
接着检查 Docker Desktop 的 Bound Socket:
Get-NetTCPConnection -State Bound | Group-Object OwningProcess | Sort-Object Count -Descending | Select-Object Name, Count
结果显示:
53020 1400
18268 111
通过 Get-Process 确认:
53020 com.docker.backend
18268 verge-mihomo
进一步统计 Docker Desktop 位于动态端口范围内的 Bound Socket:
Get-NetTCPConnection -OwningProcess 53020 -State Bound | Where-Object { $_.LocalPort -ge 49152 -and $_.LocalPort -le 50789 } | Measure-Object
结果:
Count : 1400
也就是说,Docker Desktop 的 1400 个 Bound Socket 全部位于当时配置的动态端口范围内。
虽然不能简单地把 Socket 数量直接等同于耗尽的可用端口数量,但结合 WSAENOBUFS 错误,这已经是非常明显的异常线索。
解决端口范围过小的问题
通过管理员 PowerShell 恢复常见默认范围:
netsh int ipv4 set dynamicport tcp start=49152 num=16384
如果 IPv6 也被缩小,可以检查并按需恢复:
netsh int ipv6 show dynamicport tcp
netsh int ipv6 set dynamicport tcp start=49152 num=16384
修改系统配置前,应确认没有其他软件或管理策略依赖原有端口范围。
扩大端口池可以缓解资源紧张,但如果 Docker Desktop 仍然不断占用新的端口,就只是增加了可用容量,并没有解释根本原因。
五、确认 Docker Desktop 的 Socket 持续累积
为了判断 Docker Desktop 是否存在连接资源滞留,开始持续观察 Bound Socket 的数量。
使用以下 PowerShell 命令,每隔 5 秒统计一次:
while ($true) { $p = (Get-Process com.docker.backend -ErrorAction SilentlyContinue).Id; $n = if ($p) { @(Get-NetTCPConnection -OwningProcess $p -State Bound -ErrorAction SilentlyContinue).Count } else { 0 }; Write-Host "$(Get-Date -Format HH:mm:ss) Docker Bound: $n"; Start-Sleep 5 }
这里没有固定使用进程 ID,因为 Docker Desktop 重启后 PID 会发生变化。
测试发现了一个很有规律的现象:
- 不访问容器服务时,Socket 数量基本不增长。
- 每次访问容器服务,数量都会增加。
- 重启容器也会导致数量增加。
- 停止容器后,已产生的
BoundSocket 不会立即减少。 - 重启 Docker Desktop 后,问题仍然可以再次复现。
进一步查看端口分布,发现 Docker Desktop 从 49152 开始,连续占用了大量动态端口。
这意味着 Docker Desktop 的网络活动与 Socket 增长之间存在明确关联。
不过,此时还不能断定是 Docker Desktop 自身的问题,也可能与 WSL、网络转发或其他组件有关。
六、排除 Clash Verge、Traefik 和容器配置
为了避免过早下结论,继续进行了几组对照测试。
1. 完全退出 Clash Verge
关闭 Clash Verge,并确保 Mihomo 内核进程退出。
随后继续访问 Docker 容器服务。
结果:Docker Desktop 的 Bound Socket 仍然增长。
这说明异常增长并不依赖 Clash Verge 运行。
Clash Verge 更可能是受到 Windows 网络资源不足影响的程序,而不是造成 Docker Socket 累积的直接原因。
2. Traefik 从 Host 切换到 Bridge
最初 Traefik 使用:
network_mode: host
使用 Host 网络主要是为了测试真实客户端 IP 的获取。
考虑到 Docker Desktop 在 Windows/WSL 2 上的 Host Networking 与原生 Linux 存在实现差异,怀疑过是否与 Host 网络有关。
因此将 Traefik 切换回 Bridge 网络,通过端口映射提供服务。
结果:Socket 依然随着访问量增加。
说明问题并非仅由 Traefik 的 Host Networking 引起。
3. 测试容器直接访问公网
为了排除 .test 域名及 Traefik 反向代理的影响,进一步测试容器主动向外部网站发起 HTTP 请求。
例如:
docker run --rm alpine:latest sh -c "wget -qO- http://example.com >/dev/null"
测试前应确保镜像已经存在于本地,避免首次拉取镜像产生额外网络活动,干扰观察结果。
测试发现:容器主动访问公网,同样会使 Docker Desktop 的 Bound Socket 增长。
这一步很关键。
因为此前的测试主要涉及浏览器访问容器,而这次是容器主动访问外部网络。
两种方向的网络请求都能触发增长,意味着问题可能位于 Docker Desktop 更底层的网络处理机制中,而不只是 Traefik 的请求转发。
七、检查 WSL,找到突破口
在排除了几个主要干扰因素后,开始检查 WSL 版本。
执行:
wsl --version
得到:
WSL 版本: 2.3.26.0
内核版本: 5.15.167.4-1
再检查 WSL 配置文件 .wslconfig:
[wsl2]
memory=8GB
processors=2
swap=1
swapFile=D:\\ProgramData\\WSL2\\swap\\swap.vhdx
配置中没有显式启用 networkingMode=mirrored,因此没有直接证据表明故障与 Mirrored Networking 有关。
不过,WSL 版本相对较旧,而 Docker Desktop 已经升级到 4.94.0。
考虑到 Docker Desktop 的 Linux 容器依赖 WSL 2 运行,Windows 和 Linux 之间的网络通信涉及多个组件,旧版 WSL 与较新版 Docker Desktop 的兼容性成为值得验证的方向。
升级 WSL
执行:
wsl --update
更新完成后,先保存 WSL 内正在进行的工作,并退出 Docker Desktop,然后执行:
wsl --shutdown
最后重新启动 Docker Desktop。
八、问题解决:Socket 恢复正常回收
升级 WSL 后,重新进行相同的测试。
这次结果发生了明显变化:
Docker Desktop 的 Bound Socket 数量不再持续累积,而是随着请求动态变化。
具体表现为:
- 访问容器服务时,Socket 数量增加。
- 请求结束后,数量开始下降。
- 停止访问后,
Bound数量最终回落到 0。
这与升级前只增不减的现象形成了鲜明对比。
| 测试项目 | 升级 WSL 前 | 升级 WSL 后 |
|---|---|---|
| 访问容器服务 | Bound 持续增加 | 随请求变化 |
| 停止访问 | 长时间不回落 | 逐渐回落 |
| Socket 回收 | 表现异常 | 恢复正常 |
| 停止请求后的 Bound | 大量残留 | 可以归零 |
| 动态端口资源 | 持续受到占用压力 | 不再出现相同累积现象 |
由此可以确认,升级 WSL 后,Docker Desktop 的 TCP Socket 持续累积现象已经消失。
需要说明的是,升级 WSL 同时会更新相关组件并重启网络环境,因此这次测试无法精确定位到某个 WSL 内核补丁或 Docker 网络模块。
但从前后的对照结果来看,问题高度可能与旧版 WSL 网络组件及其和 Docker Desktop 的协作有关。
至于最初的 Clash Verge WSAENOBUFS 错误,还应在正常使用一段时间后确认是否完全不再复现。
九、遇到类似问题,建议如何排查?
如果你也在 Windows 上使用 Docker Desktop,并且遇到以下现象:
- 浏览器间歇性打不开网页。
- Clash Verge、v2rayN 或其他网络程序出现 Socket 资源不足错误。
- MySQL、Redis 或其他 TCP 客户端偶发连接失败。
com.docker.backend.exe占用大量BoundSocket。- Docker 容器访问越频繁,端口占用越多。
可以优先按以下顺序检查。
第一步:检查 TCP 状态和进程占用。
Get-NetTCPConnection | Group-Object State | Sort-Object Count -Descending | Select-Object Name, Count
Get-NetTCPConnection -State Bound | Group-Object OwningProcess | Sort-Object Count -Descending | Select-Object Name, Count
第二步:检查动态端口范围。
netsh int ipv4 show dynamicport tcp
确认是否被异常缩小。
第三步:观察 Docker Socket 是否正常回收。
while ($true) { $p = (Get-Process com.docker.backend -ErrorAction SilentlyContinue).Id; $n = if ($p) { @(Get-NetTCPConnection -OwningProcess $p -State Bound -ErrorAction SilentlyContinue).Count } else { 0 }; Write-Host "$(Get-Date -Format HH:mm:ss) Docker Bound: $n"; Start-Sleep 5 }
重点不是某一瞬间的数量,而是停止请求后是否能够回落。
第四步:检查 WSL 版本并考虑升级。
wsl --version
wsl --update
如果问题仍然存在,再继续检查 Docker Desktop 的网络配置、代理设置、版本问题及相关公开 Bug。
十、总结
这次排查最初从 Clash Verge 的连接错误开始,经过 TCP 状态统计、进程定位、动态端口检查、Docker 网络模式对照、容器出入站请求测试,最终通过升级 WSL 消除了 Docker Desktop 的 Socket 持续累积现象。
其中有三个值得记住的经验。
第一,报错的程序不一定是问题的来源。 Clash Verge 报出了 Socket 资源不足,但真正异常的是 Docker Desktop 进程持有的大量 TCP Socket。
第二,扩大动态端口池不等于修复资源泄漏。 将端口数量从 1638 恢复到 16384 有助于缓解资源不足,但不能解决 Socket 只增不减的问题。
第三,排查 Docker Desktop 网络问题时,不要忽略 WSL。 Docker Desktop、WSL 和 Windows 网络栈是相互协作的系统。即使 Docker Desktop 已经是新版本,底层 WSL 组件仍可能影响网络行为。
这次案例中,升级 WSL 后,com.docker.backend.exe 的 Bound Socket 恢复正常回收,是整个排查过程中最有说服力的结果。
如果 Docker Desktop 在 Windows 上持续占用大量动态端口,不妨先检查 WSL 版本,再决定是否需要重装 Docker、重置网络或修改业务容器。
本文基于一次真实的 Windows、Docker Desktop 和 Clash Verge 故障排查整理。不同系统版本、Docker 网络模式和代理环境可能产生不同的表现,文中的解决方法并不保证适用于所有 WSAENOBUFS 错误。
参考资料