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

如何在R中关闭SSH隧道连接?(未知PID场景)

问题描述

我用以下R代码创建SSH隧道连接:

target <- paste0("host:3306")
ProxySev <- "name@1.1.1.1"
keyfile <- "path/to/key"
port <- 3307

cmd <- paste0('ssh::ssh_tunnel(ssh::ssh_connect(host = "', 
              ProxySev, '", keyfile ="', keyfile, '"), port = ', port, ', target = "', target, '")')

pid <- sys::r_background(
  args = c("-e", cmd),
  std_out = TRUE,
  std_err = TRUE
)

目前我用tools::pskill(pid)关闭连接,但如果丢失了pid(比如未正确关闭旧连接就重新运行代码),会遇到端口占用错误:

Error: System failure for: bind() (Only one usage of each socket address (protocol/network address/port) is normally permitted)
Execution halted
Warning message:
Disconnecting from unused ssh session. Please use ssh_disconnect()

想问:这种场景下怎么用ssh_disconnect()?或者怎么获取该后台进程的pid?


解决方案

一、获取占用目标端口的后台进程PID

如果丢失了pid,可以通过端口号反查对应的进程:

  • Windows系统:打开命令提示符执行:
    netstat -ano | findstr :3307
    
    找到状态为LISTENING的行,最后一列就是进程PID,再用tools::pskill(找到的PID)关闭进程。
  • Linux/macOS系统:打开终端执行:
    lsof -i :3307 | grep LISTEN
    
    输出结果的第二列就是PID,同样用tools::pskill(PID)关闭进程。

二、修改代码,用ssh_disconnect()正确关闭连接

当前代码把SSH隧道逻辑放在独立的后台进程中,无法直接跨进程调用ssh_disconnect()。可以调整代码逻辑,让后台进程自动处理会话断开:

# 改写后台执行的命令,添加会话自动断开逻辑
cmd <- paste0('
# 建立SSH会话并保存
session <- ssh::ssh_connect(host = "', ProxySev, '", keyfile ="', keyfile, '")
# 设置退出时自动断开会话
on.exit(ssh::ssh_disconnect(session))
# 启动隧道
ssh::ssh_tunnel(session, port = ', port, ', target = "', target, '")
')

pid <- sys::r_background(
  args = c("-e", cmd),
  std_out = TRUE,
  std_err = TRUE
)

这样后台进程无论正常结束还是被中断,都会自动调用ssh_disconnect()关闭SSH会话,避免残留无效连接。

如果已经有后台进程在运行且未保存PID,只能通过端口查PID后终止进程——因为后台R进程与当前会话完全独立,无法直接获取它内部的session对象来调用ssh_disconnect()。

三、预防端口占用的前置检查

可以在运行隧道创建代码前,自动检查端口是否被占用并清理:

# 检查端口是否被占用(Windows版本,Linux/macOS可替换为对应命令)
check_and_kill_port <- function(port) {
  # 获取端口占用信息
  port_info <- system2("netstat", args = c("-ano", "|", "findstr", paste0(":", port)), stdout = TRUE)
  if (length(port_info) > 0) {
    # 提取PID
    pid <- as.integer(sub(".*\\s([0-9]+)$", "\\1", port_info))
    # 终止进程
    tools::pskill(pid)
    Sys.sleep(1) # 等待进程完全关闭
  }
}

# 先清理占用3307端口的进程
check_and_kill_port(3307)

# 再执行隧道创建代码
target <- paste0("host:3306")
ProxySev <- "name@1.1.1.1"
keyfile <- "path/to/key"
port <- 3307

cmd <- paste0('
session <- ssh::ssh_connect(host = "', ProxySev, '", keyfile ="', keyfile, '")
on.exit(ssh::ssh_disconnect(session))
ssh::ssh_tunnel(session, port = ', port, ', target = "', target, '")
')

pid <- sys::r_background(
  args = c("-e", cmd),
  std_out = TRUE,
  std_err = TRUE
)

内容的提问来源于stack exchange,提问作者Art

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 02:01:14