ssh2-promise两次exec()后SSH连接断开问题求助
排查与解决ssh2-promise两次exec连接断开问题
我帮你分析下你遇到的这个问题,结合你的代码和ssh2-promise的工作机制,大概率是这几个原因导致的,给你对应的解决思路:
一、先看代码里的明显错误
在你复用连接的代码版本中,first函数resolve的是命令执行的data结果,但在wrapper里却试图把response.connection传给second——这完全是逻辑错误,exec返回的data里根本没有connection属性!second函数其实还是在复用类里的全局connection,但这个连接可能在第一次exec后已经被意外关闭了。
二、核心问题分析
ssh2-promise作为ssh2的封装库,默认情况下可能在单次命令执行后,没有正确保持连接活跃;或者你的服务器SSH配置限制了单个连接的会话次数、空闲超时时间,导致第二次调用时连接已被服务器主动关闭。
另外,新建连接的方式也失败,可能是服务器的SSH服务短时间内收到两次连接请求,被当成攻击拦截了(比如MaxStartups参数限制未认证连接数)。
三、具体解决方案
方案1:正确复用连接,显式管理连接生命周期
改用async/await简化代码,显式控制连接的建立与关闭,避免依赖库的自动行为:
function MyClass(config) { const SSH = require('ssh2-promise'); const connection = new SSH(config); async function first() { // 显式建立连接,确保连接状态可控 await connection.connect(); const data = await connection.exec('some_command_to_exec'); return data; } async function second() { // 直接复用已建立的连接 const data = await connection.exec('other_command_to_exec'); return data; } this.wrapper = async () => { try { const firstRes = await first(); console.log('首次命令结果:', firstRes); const secondRes = await second(); console.log('二次命令结果:', secondRes); } catch (err) { console.error('执行出错:', err); } finally { // 所有命令执行完成后,手动关闭连接 await connection.close(); } }; }
方案2:新建连接时规避服务器限制
如果必须每次命令新建连接,添加连接延迟和keepalive配置,避免被服务器拦截:
function MyClass(config) { const SSH = require('ssh2-promise'); async function runCommand(command) { const connection = new SSH({ ...config, keepaliveInterval: 10000, // 每10秒发送心跳保持连接 keepaliveCountMax: 3 }); try { await connection.connect(); return await connection.exec(command); } finally { await connection.close(); } } this.wrapper = async () => { try { const firstRes = await runCommand('some_command_to_exec'); console.log('首次命令结果:', firstRes); // 添加1秒延迟,避免服务器拒绝连续连接请求 await new Promise(resolve => setTimeout(resolve, 1000)); const secondRes = await runCommand('other_command_to_exec'); console.log('二次命令结果:', secondRes); } catch (err) { console.error('执行出错:', err); } }; }
方案3:改用原生ssh2库(规避封装层bug)
如果ssh2-promise的封装有潜在问题,直接用底层ssh2库实现,更精准控制连接:
const { Client } = require('ssh2'); function MyClass(config) { // 封装命令执行函数 function executeCommand(conn, command) { return new Promise((resolve, reject) => { conn.exec(command, (err, stream) => { if (err) return reject(err); let output = ''; stream.on('data', chunk => output += chunk); stream.on('end', () => resolve(output.trim())); stream.on('error', reject); }); }); } this.wrapper = async () => { const conn = new Client(); try { // 显式建立连接 await new Promise((resolve, reject) => { conn.on('ready', resolve); conn.on('error', reject); conn.connect(config); }); const firstRes = await executeCommand(conn, 'some_command_to_exec'); console.log('首次命令结果:', firstRes); const secondRes = await executeCommand(conn, 'other_command_to_exec'); console.log('二次命令结果:', secondRes); } catch (err) { console.error('执行出错:', err.message); } finally { conn.end(); } }; }
四、服务器端配置检查
如果以上方案都无效,检查你的服务器SSH配置(通常在/etc/ssh/sshd_config):
- 查看
ClientAliveInterval和ClientAliveCountMax,确保服务器不会主动关闭空闲连接; - 调整
MaxStartups参数(比如设置为MaxStartups 10:30:100),避免短时间连接被拦截; - 确认
PasswordAuthentication为yes(如果用密码登录),且你的用户名在AllowUsers列表中。
内容的提问来源于stack exchange,提问作者Rafael Sofi-zada
相关产品推荐
相关产品推荐

