如何在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 :3307LISTENING的行,最后一列就是进程PID,再用tools::pskill(找到的PID)关闭进程。 - Linux/macOS系统:打开终端执行:
输出结果的第二列就是PID,同样用lsof -i :3307 | grep LISTENtools::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
相关产品推荐
相关产品推荐

