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

如何在Rust中启动独立运行的子进程/命令?

问题分析

你的核心问题是:Rust进程启动WebSocket服务器后,服务器子进程继承了Rust进程的标准输出/错误流,导致Electron的child_process.spawn()实例一直认为子进程还在运行(因为管道持续有输出),从而无法触发close事件,Promise永远无法resolve。同时你需要Rust进程在确认服务器启动成功/失败后立即退出,而WebSocket服务器持续后台运行。

解决方案

要实现这个目标,我们需要做两件关键事情:

  1. 分离子进程与Rust进程的IO流:让WebSocket服务器的输出不再流向Electron的监听管道,避免干扰Electron判断Rust进程是否退出。
  2. 让子进程脱离父进程控制:确保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进程退出后它能继续后台运行。

验证效果

修改后,Electron的activate()方法会:

  1. 收到Rust进程输出的Activating和Server process id is xxx(或启动失败的错误日志)。
  2. Rust进程立即退出,触发close事件,Promise根据退出码resolve为true(成功)或false(失败)。
  3. WebSocket服务器会在后台持续运行,直到你通过server-stop命令关闭它。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 10:18:39