最近在 Windows 上使用 VS Code Remote-SSH 连接 Linux 服务器时,遇到了一个比较隐蔽的问题:普通 SSH 登录基本正常,但 VS Code Remote-SSH 会直接失败,日志中出现:
text
Error: remote port forwarding failed for listen port 10802
过程试图写入的管道不存在。
Failed to parse remote port from server output
一开始容易把注意力放在 VS Code Server、SSH 客户端、Windows 管道或者远程安装脚本上,但真正的根因其实是:
SSH 配置中的
RemoteForward端口已经被另一个 SSH 会话占用,而ExitOnForwardFailure yes又会让整个 SSH 连接因此直接退出。
本文记录完整的排查过程,以及后续遇到的 VS Code Remote-SSH / Dev Containers 配置兼容问题。
1 问题现象
本地环境大致如下:
- Windows
- VS Code
- Remote-SSH
- Git for Windows 自带 OpenSSH
- 远端 Linux 服务器
- SSH 配置中使用
RemoteForward - 远端通过
socat将代理端口转给 Docker 容器
SSH 配置类似:
sshconfig
Host my-server
HostName <SERVER_IP>
Port <SSH_PORT>
User <USERNAME>
IdentityFile "C:\Users\<USER>\.ssh\id_rsa"
RemoteForward 10802
ExitOnForwardFailure yes
ServerAliveInterval 30
ServerAliveCountMax 3
VS Code Remote-SSH 日志中,真正关键的错误是:
text
Error: remote port forwarding failed for listen port 10802
后面的:
text
过程试图写入的管道不存在。
Failed to parse remote port from server output
实际上只是 SSH 进程提前退出后的连锁报错。
2 先确认 SSH 客户端是否正常
Remote-SSH 会尝试在本机 PATH 中寻找 ssh.exe,日志里可能会看到大量:
text
Checking ssh with "C:\...\ssh.exe -V"
Got error from ssh: ... ENOENT
这些通常不是问题。
只要最终能找到某个可用 SSH,例如:
text
C:\Program Files\Git\usr\bin\ssh.exe
并成功输出版本:
text
OpenSSH_x.x
就说明本机 SSH 客户端本身是正常的。
所以排查重点应该放到:
text
remote port forwarding failed
而不是前面那些 ENOENT。
3 检查远端端口是否已经被占用
在远端服务器执行:
bash
sudo ss -lntp | grep 10802
如果看到类似:
text
LISTEN 0 128 127.0.0.1:10802 0.0.0.0:* users:(("sshd",pid=123456,fd=9))
LISTEN 0 128 [::1]:10802 [::]:* users:(("sshd",pid=123456,fd=7))
说明:
10802已经被某个sshd子进程监听。
也就是说,已经存在一个 SSH RemoteForward 会话。
查看具体进程:
bash
ps -fp 123456
或者:
bash
ps -aux | grep 123456
注意,下面这种写法是不对的:
bash
ps -aux | grep pid=123456
因为 ps 输出中并不会出现字面量 pid=123456。
还可以进一步检查该 SSH 会话对应的网络连接:
bash
sudo ss -tnp | grep 123456
4 为什么 RemoteForward 失败会导致整个 VS Code 连接退出
关键在于 SSH 配置中的:
sshconfig
ExitOnForwardFailure yes
它的含义是:
只要端口转发建立失败,SSH 主连接也立即失败。
因此实际流程是:
text
VS Code
↓
启动 SSH
↓
登录远端服务器
↓
申请 RemoteForward 10802
↓
10802 已经被占用
↓
RemoteForward 创建失败
↓
ExitOnForwardFailure yes
↓
SSH 直接退出
↓
VS Code 远端安装脚本被中断
↓
出现"管道不存在"
↓
Failed to parse remote port
所以真正的根因依旧只是:
text
RemoteForward 端口冲突
5 为什么换一个 RemoteForward 端口后仍然可能失败
一开始我尝试把:
text
10802
换成:
text
10812
但问题依旧存在。
这时问题就不一定是"旧 SSH 会话占了旧端口",而可能是:
VS Code Remote-SSH 自己启动了多个 SSH 连接,而每个连接都会重复执行同一个
RemoteForward。
例如:
text
VS Code SSH connection #1
↓
成功监听 10812
VS Code SSH connection #2
↓
再次请求监听 10812
↓
端口已被 connection #1 占用
↓
失败
这种情况下,不管把端口改成多少,第二个 SSH 会话都会撞到第一个。
验证方法很简单。
VS Code 连接失败以后,在服务器上立即执行:
bash
sudo ss -lntp | grep 10812
如果新端口此时已经被 sshd 占用,就说明至少有一个 SSH 会话已经成功建立了 RemoteForward。
6 一个很关键的现象:关闭另一个 VS Code 窗口后就正常了
后来发现:
只要把已经连接服务器的 VS Code 窗口关闭,新连接就能正常建立。
这说明问题和 VS Code Remote-SSH 的 SSH 会话复用方式高度相关。
也就是说,很可能存在这种情况:
text
VS Code 窗口 A
↓
SSH connection A
↓
RemoteForward 10802 成功
VS Code 窗口 B
↓
SSH connection B
↓
再次 RemoteForward 10802
↓
失败
而另一台电脑却没有这个问题,说明两台电脑的 Remote-SSH 配置或者连接模式可能不同。
7 尝试使用 useLocalServer
可以在 VS Code 的 settings.json 中尝试:
json
{
"remote.SSH.useLocalServer": true
}
其目的之一,是让多个 VS Code 远程窗口更倾向于复用本地 Remote-SSH 连接,而不是每个窗口都完全独立启动新的 SSH 会话。
对于包含固定 RemoteForward 的 SSH 配置,这样可以降低重复绑定同一远端端口的概率。
修改以后,建议:
- 完全退出所有 VS Code 窗口;
- 在任务管理器中确认没有残留的
Code.exe; - 重新打开 VS Code;
- 再尝试连接。
8 不要轻易关闭 useExecServer
排查 Remote-SSH 时,有一种常见建议是:
json
{
"remote.SSH.useExecServer": false
}
这个选项在某些 Remote-SSH 故障中可能确实有帮助。
但是如果你的使用场景是:
text
本地 VS Code
↓
Remote-SSH 到 Linux 宿主机
↓
Dev Containers
↓
Attach to Running Container
那么关闭 useExecServer 可能产生新的问题。
我实际遇到的表现是:
text
运行命令 remote-containers.attachToRunningContainerFromViewlet 错误:
出现未知错误
也就是说:
Remote-SSH 本身能用了,但无法继续 Attach 到远端 Docker 容器。
因此,对于 Remote-SSH + Dev Containers 的组合,更合适的配置是:
json
{
"remote.SSH.useLocalServer": true,
"remote.SSH.useExecServer": true
}
或者更简单:
json
{
"remote.SSH.useLocalServer": true
}
而让 useExecServer 保持默认值。
9 比较推荐的配置
最终更合理的思路是:
json
{
"remote.SSH.useLocalServer": true
}
而 SSH 配置保持:
sshconfig
Host my-server
HostName <SERVER_IP>
Port <SSH_PORT>
User <USERNAME>
IdentityFile "C:\Users\<USER>\.ssh\id_rsa"
RemoteForward 10802
ExitOnForwardFailure yes
ServerAliveInterval 30
ServerAliveCountMax 3
这样既可以尽量避免多个 VS Code 窗口反复创建 SSH RemoteForward,又不会破坏 Dev Containers over SSH 的使用。
10 更稳定的方案:将代理隧道和 VS Code SSH 解耦
如果不希望 RemoteForward 受 VS Code Remote-SSH 生命周期影响,更稳妥的方法是:
不要把
RemoteForward写在 VS Code 使用的 Host 配置中。
例如把:
sshconfig
RemoteForward 10802
ExitOnForwardFailure yes
删掉,只保留普通 SSH 配置:
sshconfig
Host my-server
HostName <SERVER_IP>
Port <SSH_PORT>
User <USERNAME>
IdentityFile "C:\Users\<USER>\.ssh\id_rsa"
ServerAliveInterval 30
ServerAliveCountMax 3
然后单独开一个 SSH 隧道:
powershell
& "C:\Program Files\Git\usr\bin\ssh.exe" `
-N `
-R 10802 `
-o ExitOnForwardFailure=yes `
-o ServerAliveInterval=30 `
-o ServerAliveCountMax=3 `
my-server
其中:
text
-N
表示只建立 SSH 隧道,不打开远端 shell。
这时整体结构变成:
text
Windows
│
├── 独立 SSH tunnel
│ ssh -N -R 10802 my-server
│
└── VS Code Remote-SSH
ssh my-server
服务器侧:
text
127.0.0.1:10802
↓
SSH tunnel
↓
Windows 本地代理
Docker 侧如果需要经过 socat:
bash
socat TCP-LISTEN:10801,bind=<DOCKER_BRIDGE_IP>,reuseaddr,fork \
TCP:127.0.0.1:10802
整体链路:
text
Docker Container
↓
<DOCKER_BRIDGE_IP>:10801
↓
socat
↓
127.0.0.1:10802
↓
SSH RemoteForward
↓
Windows
↓
本地代理
这个方案最大的优点是:
VS Code Remote-SSH 开几个窗口,都不会影响代理隧道。
对于需要长期使用代理的远程开发环境,我个人更推荐这种解耦方案。
11 排查时最有用的几个命令
检查 RemoteForward 端口:
bash
sudo ss -lntp | grep <PORT>
查看对应进程:
bash
ps -fp <PID>
检查 SSH 网络连接:
bash
sudo ss -tnp | grep <PID>
手动测试 SSH:
powershell
& "C:\Program Files\Git\usr\bin\ssh.exe" -vvv my-server
重点关注:
text
remote forward
remote port forwarding failed
Address already in use
等信息。
12 总结
这类问题最容易误导人的地方,是 VS Code 最终显示的报错往往是:
text
Failed to parse remote port
或者:
text
过程试图写入的管道不存在
但它们经常并不是根因。
如果日志前面出现:
text
remote port forwarding failed for listen port ...
优先检查:
- 远端端口是否已经被其他
sshd占用; - 是否存在旧 SSH 会话;
- VS Code 是否同时启动了多个 SSH 连接;
ExitOnForwardFailure yes是否导致整个 SSH 连接退出;remote.SSH.useLocalServer是否可以改善多窗口连接复用;- 如果还需要 Dev Containers,不要随意关闭
remote.SSH.useExecServer; - 最稳定的做法,是将代理 SSH 隧道和 VS Code Remote-SSH 分离。
最终可以把整个问题归纳为一句话:
RemoteForward 本身没有问题,真正的问题是固定远端监听端口与 VS Code 多 SSH 会话之间发生了生命周期冲突。