自动重连(实用版): pssh ssh -o ReconnectInterval=N 断线(keepalive 发送失败判定)由外层循环自动重连并重跑命令/重开shell(pssh_cmd_ssh 返回-2, pssh main 重试); 依赖 ServerAliveInterval 心跳探测; STATUS/AGENT/CHANGELOG 更新

这个提交包含在:
JGZYES
2026-08-14 14:50:49 +08:00
父节点 825a9d32e4
当前提交 a1b95df38f
修改 5 个文件,包含 42 行新增3 行删除
+5
查看文件
@@ -107,4 +107,9 @@ session ticket key 支持持久化到文件:新增 `paze_tls_config_set_session_
- `pssh ssh -A`:开 `auth-agent@openssh.com` 通道,事件循环桥接本地 agent socket 与通道(`ssh_agent_fd`)。
- 限制:PazeSSH 仅 POSIX 可用;Windows agent 为命名管道(无 select fd)运行时明确禁用并提示;无本地 agent 时报 `cannot connect local agent`
### 11. 自动重连(实用版)(✅ 已完成 2026-08-14
- `pssh ssh -o ReconnectInterval=N`:断线(keepalive 发送失败判定)由外层循环自动重连并重新执行命令/重开 shell(pssh_cmd_ssh 返回 -2)。
- 说明:依赖 ServerAliveInterval 心跳探测(建议两者搭配);恢复的是"重连+重跑",不恢复进行中 PTY 状态。
+5
查看文件
@@ -4,6 +4,11 @@
## [开发主线] — 2026-08-14
### 新增 —— 自动重连(实用版)
- `pssh ssh -o ReconnectInterval=N`:断线(keepalive 心跳发送失败判定)时由外层循环自动重连并**重新执行命令/重开 shell**(`pssh_cmd_ssh` 返回 -2 触发)。
- 说明:依赖 `ServerAliveInterval` 心跳探测(建议配 `-o ServerAliveInterval=N -o ReconnectInterval=N`);恢复的是"重连+重跑",不能恢复进行中的 PTY 状态。
### 新增 —— agent 转发(-A
- `pssh ssh -A`:打开 `auth-agent@openssh.com` 通道,事件循环把本地 agent socket 与通道双向桥接(`ssh_agent_fd` 暴露 POSIX fd)。
+1 -1
查看文件
@@ -44,7 +44,7 @@
- 通道:sessionexec/shell 真实进程执行)、direct-tcpip(出站桥接)
- **SFTP 子系统 v3**OPEN/READ/WRITE/OPENDIR/READDIR/STAT/REALPATH 等,与 OpenSSH sftp / scp -s 互操作(Git OpenSSH 实测通过)
- 客户端命令:
- `pssh ssh` — 远程 shell / 执行命令,`-L`/`-R`/`-D` 端口转发,`-J` 代理跳板(单跳)、`-W host:port` stdio 转发、`-N` 仅转发、`-A` agent 转发(POSIX;Windows agent 为命名管道不支持);config 读取 `-F` / `~/.ssh/config`OpenSSH 兼容) / `~/.pssh/config.conf``-o ServerAliveInterval=N` 空闲心跳保活(keepalive@openssh.com
- `pssh ssh` — 远程 shell / 执行命令,`-L`/`-R`/`-D` 端口转发,`-J` 代理跳板(单跳)、`-W host:port` stdio 转发、`-N` 仅转发、`-A` agent 转发(POSIX;Windows agent 为命名管道不支持);config 读取 `-F` / `~/.ssh/config`OpenSSH 兼容) / `~/.pssh/config.conf``-o ServerAliveInterval=N` 空闲心跳保活(keepalive@openssh.com`-o ReconnectInterval=N` 断线自动重连(重新执行命令/重开 shell)
- `pssh scp` — SFTP 优先(OpenSSH 9.x 默认),回退 legacy SCP;`-r` 递归、`-p` 保留时间戳
- `pssh keygen` / `keyscan` / `copy-id` — 密钥工具
+19 -1
查看文件
@@ -18,9 +18,14 @@
#ifdef _WIN32
#include <windows.h>
#define PSSH_SLEEP_MS(ms) Sleep(ms)
#else
#include <unistd.h>
#define PSSH_SLEEP_MS(ms) usleep((ms) * 1000)
#endif
int pssh_cmd_ssh(int argc, char **argv);
int pssh_ssh_reconnect_interval(void); /* apps/ssh/main.c */
int pssh_cmd_keygen(int argc, char **argv);
int pssh_cmd_keyscan(int argc, char **argv);
int pssh_cmd_copyid(int argc, char **argv);
@@ -95,7 +100,20 @@ int main(int argc, char **argv) {
usage();
return 0;
}
if (strcmp(cmd, "ssh") == 0) return pssh_cmd_ssh(argc - 1, argv + 1);
if (strcmp(cmd, "ssh") == 0) {
/* 断线自动重连(实用版):每 ReconnectInterval 秒重新调用 pssh_cmd_ssh
* (重连并重新执行命令/重开 shell)。-2 表示断线请求重连。 */
for (;;) {
int r = pssh_cmd_ssh(argc - 1, argv + 1);
if (r == -2 && pssh_ssh_reconnect_interval() > 0) {
fprintf(stderr, "pssh: connection lost, reconnecting in %d s...\n",
pssh_ssh_reconnect_interval());
PSSH_SLEEP_MS(pssh_ssh_reconnect_interval() * 1000);
continue;
}
return r;
}
}
if (strcmp(cmd, "keygen") == 0) return pssh_cmd_keygen(argc - 1, argv + 1);
if (strcmp(cmd, "keyscan") == 0) return pssh_cmd_keyscan(argc - 1, argv + 1);
if (strcmp(cmd, "copy-id") == 0 || strcmp(cmd, "copyid") == 0)
+12 -1
查看文件
@@ -55,6 +55,12 @@ typedef struct {
/* -v / -c 选项(跨 connect_target 传递) */
static int g_verbose = 0;
static const char *g_cipher = NULL;
/* -o ReconnectInterval=N:断线自动重连间隔秒(0=禁用)。断线时 pssh_cmd_ssh 返回 -2,
* 由 pssh main 循环重连。 */
static int g_reconnect = 0;
/* 供外层(pssh main)查询自动重连间隔秒 */
int pssh_ssh_reconnect_interval(void) { return g_reconnect; }
/* 选项缺少参数时的用法提示;缺参返回 -1 */
static int opt_missing(int argc, int i, const char *opt, const char *hint) {
@@ -343,6 +349,7 @@ int pssh_cmd_ssh(int argc, char **argv) {
int no_command = 0;
int agent_fwd = 0; /* -A:agent 转发(POSIX;Windows agent 为命名管道不支持) */
char stdio_host[256];
int reconnect_flag = 0; /* 断线标记(供自动重连) */
const char *user_cfg = NULL; /* -F <file>:显式指定 config 文件 */
for (int i = 1; i < argc; i++) {
@@ -445,6 +452,9 @@ int pssh_cmd_ssh(int argc, char **argv) {
else if (strncmp(kv, "ServerAliveInterval=", 20) == 0) {
server_alive = atoi(kv + 20);
}
else if (strncmp(kv, "ReconnectInterval=", 18) == 0) {
g_reconnect = atoi(kv + 18); /* 断线自动重连间隔秒 */
}
} else if (a[0] == '-') {
continue;
} else if (!host) {
@@ -625,7 +635,7 @@ int pssh_cmd_ssh(int argc, char **argv) {
time_t now = time(NULL);
if (now - last_alive >= server_alive) {
last_alive = now;
if (ssh_keepalive_send(s) < 0) { fprintf(stderr, "pssh: keepalive send failed\n"); break; }
if (ssh_keepalive_send(s) < 0) { fprintf(stderr, "pssh: keepalive send failed\n"); reconnect_flag = 1; break; }
}
}
fd_set rfds;
@@ -786,5 +796,6 @@ done:
ssh_session_free(jump);
}
ssh_session_free(s);
if (reconnect_flag && g_reconnect > 0) return -2; /* 通知上层自动重连 */
return exit_code;
}