ssh-channels-hub 0.2.0

A CLI tool for managing SSH port forwarding tunnels with auto-reconnect
Documentation
# 工作流程文档

## 1. 应用程序启动流程

### 1.1 初始化阶段

```
1. 解析命令行参数
   ├── 命令类型 (start/stop/restart/status/validate)
   ├── 配置文件路径 (可选)
   └── 调试标志 (可选)

2. 初始化日志系统
   ├── 设置日志级别 (根据 --debug 标志)
   ├── 配置日志格式
   └── 初始化 tracing subscriber

3. 加载配置文件
   ├── 确定配置文件路径
   │   ├── 使用 --config 指定的路径
   │   └── 或使用默认路径
   ├── 读取文件内容
   ├── 解析 TOML
   └── 验证配置有效性
```

### 1.2 命令处理流程

#### Start 命令

```
1. 加载配置
   2. 创建 ServiceManager
   3. 调用 ServiceManager::start()
   ├── 设置状态为 "Starting"
   ├── 遍历所有 channels 配置
   │   ├── 创建 SshManager
   │   ├── 调用 SshManager::start()
   │   └── 记录启动结果
   ├── 更新状态为 "Running" 或 "Error"
   └── 返回结果
   4. 如果前台模式(默认)
   ├── 绑定 IPC 监听(动态端口),写入 .port、.pid
   ├── 等待 Ctrl+C 信号
   └── 调用 ServiceManager::stop(),清理 .port、.pid
   5. 如果 daemon 模式(start -D / --daemon)
   ├── 子进程以非 daemon 方式启动,绑定 IPC,写入 .port、.pid
   ├── 父进程退出
   └── 子进程持续运行直至收到 stop 或崩溃
```

#### Stop 命令

```
1. 读取 .port 文件(与 --config 同目录)
   2. 通过 TCP 连接 IPC 端口,发送 "stop\n"
   3. 守护进程收到后取消 CancellationToken,执行 ServiceManager::stop()
   ├── 设置状态为 "Stopping"
   ├── 遍历所有 SshManager,发送关闭信号
   ├── 关闭本地 TCP 监听、等待任务结束
   ├── 删除 .port、.pid
   └── 进程退出
   4. 若 .port 不存在或连接失败,则仅尝试删除 .port、.pid
```

#### Restart 命令

```
1. 若 .port 存在,通过 IPC 发送 stop,等待守护进程退出
   2. 清理 .port、.pid(若仍存在)
   3. 以 daemon 方式重新启动(spawn 子进程执行 start)
```

#### Status 命令

```
1. 读取 .port 文件,通过 TCP 连接 IPC,发送 "status\n"
   2. 若连接成功,接收 TOML 格式状态(state, active_channels, total_channels)
   3. 显示状态信息
   ├── 服务状态(含 emoji)
   ├── 活动/总 channels 数
   ├── 配置文件路径、PID
   └── 已配置 channel 列表(name, local_port -> dest_host:dest_port)
   4. 若 .port 不存在或连接失败,显示 "Stopped" 及配置路径
```

#### Validate 命令

```
1. 加载配置文件
   2. 验证配置
   ├── 检查 TOML 语法
   ├── 检查必需字段
   ├── 检查字段类型
   └── 检查 channel 名称唯一性
   3. 显示验证结果
```

## 2. SSH 连接建立流程

### 2.1 连接阶段

```
1. SshManager::start() 被调用
   2. 创建关闭信号通道
   3. 启动独立任务
   4. 调用 connect_and_manage_channel()
   ├── 构建重试策略
   └── 使用 backon 重试连接
   5. establish_connection()
   ├── 创建 SSH 客户端配置
   ├── 创建 ClientHandler
   ├── 连接到服务器
   │   └── russh::client::connect()
   ├── 认证
   │   ├── 密码认证
   │   └── 密钥认证
   └── 打开 channel
       ├── Session channel
       └── Direct-TCPIP channel
```

### 2.2 channel 管理流程

#### Session channel

```
1. channel_open_session()
   2. 检查是否有命令参数
   ├── 有命令: exec(command)
   └── 无命令: request_pty() + shell()
   3. 启动 channel 数据处理任务
   ├── 监听 channel 消息
   ├── 处理数据
   └── 检测 channel 关闭
```

#### Direct-TCPIP channel

```
1. 在本地绑定 TcpListener(listen_host:local_port)
   2. 循环 accept 新连接
   3. 对每个新连接:
   ├── channel_open_direct_tcpip(目标地址, 目标端口, 源地址, 源端口)
   ├── 使用 copy_bidirectional 在本地 TcpStream 与 ChannelStream 间转发数据
   └── 连接关闭时关闭 channel
   4. 收到停止信号时取消 accept 循环并退出
```

## 3. 重连流程

### 3.1 连接断开检测

```
连接/channel 错误发生
   错误被捕获
   记录错误日志
   返回到重连逻辑
```

### 3.2 重连执行

```
1. 计算重试延迟
   ├── 指数退避策略
   │   └── delay = min(initial * 2^attempt, max_delay)
   └── 固定间隔策略
       └── delay = initial_delay
   2. 检查重试限制
   ├── 如果 max_retries > 0
   │   └── 检查是否超过限制
   └── 如果 max_retries == 0
       └── 无限重试
   3. 等待延迟时间
   4. 记录重试日志
   5. 重新建立连接
   └── 回到连接建立流程
```

### 3.3 重连策略示例

**指数退避** (initial=1s, max=30s):

```
尝试 1: 等待 1s
尝试 2: 等待 2s
尝试 3: 等待 4s
尝试 4: 等待 8s
尝试 5: 等待 16s
尝试 6+: 等待 30s (上限)
```

**固定间隔** (delay=5s):

```
尝试 1: 等待 5s
尝试 2: 等待 5s
尝试 3: 等待 5s
...
```

## 4. 关闭流程

### 4.1 正常关闭

```
1. 收到关闭信号 (Ctrl+C 或 stop 命令)
   2. ServiceManager::stop()
   ├── 设置状态为 "Stopping"
   ├── 遍历所有 SshManager
   │   └── SshManager::stop()
   │       └── 发送关闭信号到任务
   └── 清空管理器列表
   3. 每个 SshManager 任务
   ├── 收到关闭信号
   ├── 关闭 SSH 连接
   ├── 清理资源
   └── 退出任务
   4. 设置状态为 "Stopped"
   5. 退出应用程序
```

### 4.2 异常关闭

```
1. 未捕获的 panic
   2. 任务终止
   3. 连接自动关闭
   4. 其他 channels 继续运行
   5. 服务状态可能变为 "Error"
```

## 5. 并发执行流程

### 5.1 多 channels 并发

```
主任务
  ├── channel 1 任务 ──┐
  ├── channel 2 任务 ──┤
  ├── channel 3 任务 ──┼──> 独立运行,互不阻塞
  └── channel N 任务 ──┘
```

### 5.2 channel 内部并发

```
SshManager 任务
  ├── 连接管理任务
  ├── channel 数据处理任务 1
  ├── channel 数据处理任务 2
  └── 关闭信号监听任务
```

### 5.3 同步点

- **启动**: 所有 channels 并行启动,不等待其他 channels
- **停止**: 等待所有 channels 完成关闭
- **状态查询**: 需要锁定状态进行读取

## 6. 错误处理流程

### 6.1 错误分类和处理

```
配置错误
  └── 立即失败,不启动服务

连接错误
  ├── 临时性错误 → 重试
  └── 永久性错误 → 记录错误,跳过该 channel

认证错误
  └── 记录错误,跳过该 channel(不重试)

channel 错误
  └── 重试(重新打开 channel)
```

### 6.2 错误传播

```
底层错误 (russh::Error)
   ↓
转换为 AppError
   ↓
添加上下文信息
   ↓
记录日志
   ↓
返回给调用者
   ↓
决定是否重试
```

## 7. 日志记录流程

### 7.1 日志级别使用

- **trace**: 详细的函数调用和状态变化
- **debug**: channel 消息、连接细节
- **info**: 重要事件(连接建立、channel 打开)
- **warn**: 非致命问题(连接关闭、重连)
- **error**: 错误条件(连接失败、认证失败)

### 7.2 结构化日志

```
info!(
    channel = %config.name,    // channel 名称
    host = %config.host,       // host 地址
    port = config.port,        // 端口号
    "Establishing SSH connection"
)
```

### 7.3 日志输出

- **控制台**: 默认输出到 stderr
- **文件**: 未来可能支持日志文件
- **格式**: 由 tracing-subscriber 控制

## 8. 性能优化流程

### 8.1 资源管理

```
连接建立
  ├── 使用 Arc 共享配置(避免复制)
  ├── 及时释放不需要的资源
  └── 使用有界通道(防止内存泄漏)
```

### 8.2 并发优化

```
异步 I/O
  ├── 所有网络操作异步
  ├── 避免阻塞操作
  └── 使用 tokio::spawn_blocking 处理阻塞操作
```

### 8.3 重连优化

```
智能重试
  ├── 指数退避避免资源浪费
  ├── 限制最大重试次数
  └── 可配置的重试策略
```