基于JSch实现双网关SSH隧道连接的问题求助
解决双网关SSH隧道连接目标主机的问题
我来帮你搞定这个双网关隧道的问题!先看你遇到的报错:PortForwardingL: local port 127.0.0.1:2233 cannot be bound,原因很直接——你重复使用了同一个本地端口2233做两次本地端口转发,第一次绑定后第二次再用就会触发端口占用的冲突。
问题根源分析
你的代码里:
- 第一个会话(连接Gateway1)把本地2233端口转发到Gateway2的22端口:
session.setPortForwardingL(2233, Gateway2, 22) - 第二个会话(通过本地2233连Gateway2)又试图把同一个本地2233端口转发到endpoint的22端口:
secondSession.setPortForwardingL(2233, endpoint, 22)
同一台机器上的本地端口不能被多个进程(或同一个进程的多个转发)重复绑定,这就是报错的核心原因。另外还有个小问题:你最后执行命令用的是secondSession的channel,但实际上你要连接的是finalSession对应的endpoint主机,这也会导致命令执行到错误的机器上。
修复后的代码
下面是修正后的完整代码,我标注了关键修改点:
import com.jcraft.jsch.*; import java.io.InputStream; public class Main { public static void main(String[] args){ Main t=new Main(); try{ t.go(); } catch(Exception ex){ ex.printStackTrace(); } } public void go() throws Exception{ StringBuilder outputBuffer = new StringBuilder(); String Gateway1="192.168.0.101"; // First level gateway String user1="root"; String password="12qwaszx"; String Gateway2="192.168.0.102"; // Second level gateway String user2 = "root"; String secondPassword="12qwaszx"; String endpoint = "10.81.77.52"; // Target host String finaluser="admin"; String finaluserpass="admin"; JSch jsch=new JSch(); // 第一步:连接第一个网关Gateway1,并转发本地2233到Gateway2的22端口 Session session=jsch.getSession(user1, Gateway1, 22); session.setPassword(password); localUserInfo lui=new localUserInfo(); session.setUserInfo(lui); session.setConfig("StrictHostKeyChecking", "no"); session.setPortForwardingL(2233, Gateway2, 22); session.connect(); System.out.println("Connected to Gateway1, port 2233 forwarded to Gateway2:22"); // 第二步:通过本地2233连接Gateway2,并转发本地2234到endpoint的22端口(换了新端口!) Session secondSession = jsch.getSession(user2, "localhost", 2233); secondSession.setPassword(secondPassword); secondSession.setUserInfo(lui); secondSession.setConfig("StrictHostKeyChecking", "no"); secondSession.setPortForwardingL(2234, endpoint, 22); // 修改:用2234代替2233 secondSession.connect(); System.out.println("Connected to Gateway2, port 2234 forwarded to endpoint:22"); // 第三步:通过本地2234连接目标主机endpoint Session finalSession = jsch.getSession(finaluser, "localhost", 2234); // 修改:连接2234端口 finalSession.setPassword(finaluserpass); finalSession.setUserInfo(lui); finalSession.setConfig("StrictHostKeyChecking", "no"); finalSession.connect(); System.out.println("Connected to target endpoint: " + endpoint); // 关键修改:用finalSession打开通道,执行目标主机上的命令 Channel channel=finalSession.openChannel("exec"); ((ChannelExec)channel).setCommand("show system information | match \"System Name\""); channel.setInputStream(null); InputStream stdout=channel.getInputStream(); channel.connect(); if (channel.isConnected()){ System.out.println("Command channel connected"); } while (true) { byte[] tmpArray=new byte[1024]; while(stdout.available() > 0){ int i=stdout.read(tmpArray, 0, 1024); if(i<0)break; outputBuffer.append(new String(tmpArray, 0, i)); } if(channel.isClosed()){ System.out.println("exit-status: "+channel.getExitStatus()); break; } // 加个短睡眠,避免循环占用CPU Thread.sleep(100); } // 按顺序关闭资源:通道 -> 最终会话 -> 第二个网关会话 -> 第一个网关会话 stdout.close(); channel.disconnect(); finalSession.disconnect(); secondSession.disconnect(); session.disconnect(); System.out.println("\nCommand output:"); System.out.print(outputBuffer.toString()); } class localUserInfo implements UserInfo { String passwd; public String getPassword(){ return passwd; } public boolean promptYesNo(String str){return true;} public String getPassphrase(){ return null; } public boolean promptPassphrase(String message){return true; } public boolean promptPassword(String message){return true;} public void showMessage(String message){} } }
关键修改说明
- 端口冲突修复:第二次端口转发改用本地端口2234,避免和第一次的2233重复绑定
- 命令执行会话修正:使用
finalSession打开执行命令的通道,确保命令在目标主机endpoint上运行 - 资源关闭顺序:按创建的逆序关闭会话和通道,避免资源泄漏
- 增加状态打印:方便你排查每一步的连接情况
额外注意事项
- 确保Gateway1能访问Gateway2,Gateway2能访问endpoint的22端口(SSH服务端口)
StrictHostKeyChecking设为no虽然方便测试,但生产环境建议改为ask或yes,避免中间人攻击风险- 如果本地端口2233、2234也被其他程序占用,可以换成其他未被使用的端口(比如2235、2236等)
内容的提问来源于stack exchange,提问作者Hugo Abreu
相关产品推荐
相关产品推荐

