如何在Rust中启动独立运行的子进程/命令?
问题分析
你的核心问题是:Rust进程启动WebSocket服务器后,服务器子进程继承了Rust进程的标准输出/错误流,导致Electron的child_process.spawn()实例一直认为子进程还在运行(因为管道持续有输出),从而无法触发close事件,Promise永远无法resolve。同时你需要Rust进程在确认服务器启动成功/失败后立即退出,而WebSocket服务器持续后台运行。
解决方案
要实现这个目标,我们需要做两件关键事情:
- 分离子进程与Rust进程的IO流:让WebSocket服务器的输出不再流向Electron的监听管道,避免干扰Electron判断Rust进程是否退出。
- 让子进程脱离父进程控制:确保Rust进程退出后,WebSocket服务器不会被终止,能独立运行。
修改Rust代码
首先更新你的start_server_process函数,添加跨平台的进程分离和IO重定向逻辑:
1. 添加依赖
在Cargo.toml中补充所需依赖:
[dependencies] ws = "0.9" libc = "0.2" winapi = { version = "0.3", features = ["winbase", "processthreadsapi"] }
2. 修改启动逻辑
extern crate ws; extern crate libc; extern crate winapi; use std::env; use std::process::{Command, Stdio}; use ws::{connect, CloseCode}; use winapi::um::winbase::{CREATE_NEW_PROCESS_GROUP, DETACHED_PROCESS}; fn main() { let args: Vec<String> = env::args().collect(); let command = &args[1]; if command == "activate" { println!("Activating"); stop_server_process(); start_server_process(); } } fn start_server_process() { let mut command; if cfg!(windows) { command = Command::new(".\\node_modules\\.bin\\ws.cmd"); // Windows:让进程脱离父进程控制台,创建独立进程组 command.creation_flags(CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS); // 重定向所有IO到NUL,避免输出干扰Electron command.stdout(Stdio::null()); command.stderr(Stdio::null()); command.stdin(Stdio::null()); } else { command = Command::new("./node_modules/.bin/ws"); // Unix:创建新会话,让子进程脱离父进程控制终端 command.before_exec(|| { unsafe { if libc::setsid() == -1 { return Err(std::io::Error::last_os_error()); } } Ok(()) }); // 重定向所有IO到/dev/null command.stdout(Stdio::null()); command.stderr(Stdio::null()); command.stdin(Stdio::null()); } command.arg("--websocket"); command.arg("test/mock-api/ws-server.js"); command.arg("-p"); command.arg("9401"); match command.spawn() { Ok(child) => println!("Server process id is {}", child.id()), Err(e) => { println!("Server didn't start: {}", e); std::process::exit(1); }, } // Rust进程确认启动结果后立即退出 std::process::exit(0); } fn stop_server_process() { // 处理连接失败的情况,避免panic if let Err(e) = connect("ws://localhost:9401", |out| { out.send("server-stop").unwrap(); move |msg| { println!("Got message: {}", msg); out.close(CloseCode::Normal) } }) { println!("No running server found: {}", e); } }
为什么这样修改?
- IO重定向:将WebSocket服务器的stdout/stderr/stdin重定向到空设备(Windows的
NUL,Unix的/dev/null),这样服务器的输出不会再流入Electron监听的管道,Electron只会收到Rust进程的启动日志,一旦Rust进程退出,管道立即关闭,触发close事件。 - 进程分离:
- Windows下使用
DETACHED_PROCESS让服务器进程脱离父进程控制台,CREATE_NEW_PROCESS_GROUP确保它不会被Rust进程的终止信号影响。 - Unix下调用
setsid()创建新会话,让服务器进程成为会话首进程,脱离原控制终端,Rust进程退出后它能继续后台运行。
- Windows下使用
验证效果
修改后,Electron的activate()方法会:
- 收到Rust进程输出的
Activating和Server process id is xxx(或启动失败的错误日志)。 - Rust进程立即退出,触发
close事件,Promise根据退出码resolve为true(成功)或false(失败)。 - WebSocket服务器会在后台持续运行,直到你通过
server-stop命令关闭它。
内容的提问来源于stack exchange,提问作者OK sure
相关产品推荐
相关产品推荐

