C#通过SSH连接远程后建立TCP Socket超时问题排查
问题根因
代码的TCP连接发起位置错误:
- 代码中
ping操作通过SshCommand提交到远程SSH服务器执行,处于远程服务器的网络环境下,可以访问服务器内网的10.xx.xxx.xxx地址,因此执行成功。 - 后续实例化的
TcpClient运行在本地机器上下文,直接尝试连接10.xx.xxx.xxx:32060。该地址属于远程服务器侧的私网网段,本地机器无对应路由,连接必然超时。 - 手动登录SSH后能在服务器内建立Socket连接,本质是TCP连接由远程服务器发起,和本地网络环境无关。
解决方案
使用SSH本地端口转发能力,通过已建立的SSH隧道把本地端口流量转发到远程服务器可访问的目标内网地址,再让本地TcpClient连接本地转发端口即可。以你使用的SSH.NET库为例,修改后的代码如下:
using (var client = new SshClient("74.xxx.xxx.xxx", 8811, "myuser", privatekey)) { client.Connect(); string targetHost = "10.xx.xxx.xxx"; int targetPort = 32060; // 绑定本地回环地址的空闲端口,自动转发到远程侧目标内网地址 var forwardedPort = new ForwardedPortLocal("127.0.0.1", 0, targetHost, (uint)targetPort); client.AddForwardedPort(forwardedPort); forwardedPort.Start(); if (client.IsConnected && forwardedPort.IsStarted) { Console.WriteLine("ssh connected, port forward started"); // 连通性校验逻辑保持不变,在远程服务器侧执行ping SshCommand cmd = client.CreateCommand($"ping -c 3 {targetHost}"); cmd.Execute(); Console.WriteLine(cmd.Result); TcpClient tcpClient = new TcpClient(); // 连接本地转发端口,流量经SSH隧道由远程服务器发起对目标地址的连接 tcpClient.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), forwardedPort.BoundPort)); Console.WriteLine("tcp connected successfully"); // 后续直接通过tcpClient.GetStream()即可完成Socket收发,逻辑和普通TCP连接一致 } // 资源清理 forwardedPort.Stop(); client.Disconnect(); }
注意事项
- 不需要手动打开终端维持SSH会话,上述代码运行时会自动维持SSH隧道和转发规则,程序退出后隧道自动断开。
- 如果需要绑定固定本地端口,把
ForwardedPortLocal构造函数的第二个参数0替换为你想用的未被占用的端口号即可。 - 请使用2020年后发布的SSH.NET稳定版本,旧版本存在端口转发功能异常的已知问题。
内容的提问来源于stack exchange,提问作者atkayla
相关产品推荐
相关产品推荐

