You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 09:21:48