使用Paramiko实现SSH二次连接时遇OSError: Socket is closed问题求助
解决SSH二次跳转的Socket关闭问题
问题背景
已编写Python代码通过Paramiko成功连接到服务器10.218.16.16,现在希望在同一代码中实现从该服务器跳转至hadoopc1h1(模拟Putty中执行ssh user2@hadoopc1h1并输入密码的操作),但运行代码时出现错误:
OSError: Socket is closed
原代码及问题点
第一部分:连接第一台服务器的代码
import paramiko # Set up SSH client ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # Set up connection details ip = "10.218.16.16" user = "user1" password = "234!!!lem600@HW" # Connect to server try: ssh.connect(ip, username=user, password=password) print(f"Successfully logged in as {ssh.exec_command('whoami')[1].read().decode().strip()}") except paramiko.ssh_exception.AuthenticationException: print("Authentication failed") except paramiko.ssh_exception.SSHException: print("Unable to establish SSH connection") finally: ssh.close()
第二部分:尝试跳转的代码
ip2 = "hadoopc1h1" user2 = "user2" password2 = "235!!!lem600@HW" try: # Execute ssh command to connect to second server _, stdout, _ = ssh1.exec_command(f'ssh {user2}@{ip2}') stdin = stdout.channel.makefile("wb") stdin.write(f"{password2}\n".encode()) stdin.flush() # Print output of ssh command to second server print(stdout.read().decode())
核心问题
- 变量名错误:跳转代码中使用
ssh1,但第一部分连接的客户端实例是ssh,变量名不匹配。 - Socket提前关闭:第一部分代码的
finally块中直接调用了ssh.close(),导致执行跳转操作时,第一台服务器的SSH连接已关闭,触发Socket is closed错误。 - 交互逻辑不可靠:通过
exec_command执行ssh后直接写入密码的方式,无法确保命令执行到需要输入密码的阶段,交互逻辑不稳定。
解决方案
方案1:通过Paramiko建立二级SSH连接(推荐)
利用第一台服务器的SSH连接作为跳板,直接建立到第二台服务器的SSH连接,避免交互式输入密码的问题:
import paramiko # 连接第一台服务器(跳板机) ip_jump = "10.218.16.16" user_jump = "user1" password_jump = "234!!!lem600@HW" ssh_jump = paramiko.SSHClient() ssh_jump.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: ssh_jump.connect(ip_jump, username=user_jump, password=password_jump) print(f"Successfully logged in to jump server as {ssh_jump.exec_command('whoami')[1].read().decode().strip()}") # 通过跳板机建立到目标服务器的TCP通道 transport = ssh_jump.get_transport() dest_addr = ("hadoopc1h1", 22) local_addr = (ip_jump, 22) channel = transport.open_channel("direct-tcpip", dest_addr, local_addr) # 连接目标服务器 ssh_target = paramiko.SSHClient() ssh_target.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh_target.connect("hadoopc1h1", username="user2", password="235!!!lem600@HW", sock=channel) # 验证目标服务器连接 whoami_output = ssh_target.exec_command("whoami")[1].read().decode().strip() print(f"Successfully logged in to target server as {whoami_output}") # 示例:在目标服务器执行ls命令 _, stdout, _ = ssh_target.exec_command("ls") print("Target server ls output:\n", stdout.read().decode()) except paramiko.ssh_exception.AuthenticationException: print("Authentication failed") except paramiko.ssh_exception.SSHException as e: print(f"SSH connection error: {str(e)}") finally: # 关闭所有连接 if 'ssh_target' in locals(): ssh_target.close() ssh_jump.close()
方案2:修复交互式跳转逻辑(适合简单场景)
如果必须使用交互式ssh命令,调整代码逻辑确保跳板机连接未关闭,并等待密码提示符后再输入密码:
import paramiko import time # 连接跳板机 ip_jump = "10.218.16.16" user_jump = "user1" password_jump = "234!!!lem600@HW" ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: ssh.connect(ip_jump, username=user_jump, password=password_jump) print(f"Successfully logged in to jump server as {ssh.exec_command('whoami')[1].read().decode().strip()}") # 启动交互式shell shell = ssh.invoke_shell() shell.settimeout(10) # 执行跳转命令 shell.send("ssh user2@hadoopc1h1\n") time.sleep(1) # 等待密码提示符并输入密码 while not shell.recv_ready(): time.sleep(0.5) output = shell.recv(1024).decode() if "password:" in output: shell.send("235!!!lem600@HW\n") time.sleep(1) # 读取跳转后的输出 while shell.recv_ready(): output += shell.recv(1024).decode() print("Jump output:\n", output) except paramiko.ssh_exception.AuthenticationException: print("Authentication failed") except paramiko.ssh_exception.SSHException as e: print(f"SSH connection error: {str(e)}") except Exception as e: print(f"Error: {str(e)}") finally: ssh.close()
关键说明
- 方案1优势:无需处理交互式输入,连接稳定可靠,适合后续在目标服务器执行复杂操作。
- 方案2注意事项:依赖
time.sleep()等待输出,网络延迟较高时可能失效,需根据实际情况调整等待时间。
内容的提问来源于stack exchange,提问作者Pathi
相关产品推荐
相关产品推荐

