替代 stdin/stdout 与子进程通信?

问题描述 投票:0回答:1

我正在生成一个子进程,我控制其内部代码,并且需要子进程和主机进程之间的双向通信。

现在我有一个简单的实现,让主机通过 stdin 向子进程发送 JSON 并通过 stdout 接收 JSON 响应。

static CHILD_CODE: &str = r#"
  /* To parent */
  function send_to_host(data) {
    process.stdout.write(JSON.stringify(data))
    process.stdout.write('\n')
  }

  /* From parent */
  process.stdin.on('data', data => {
    const parsed = JSON.parse(data)
    send_to_host(parsed)
  })

  process.stdin.on('end', () => {
    process.exit(0)
  });

  send_to_host({ baz: 'buz' })
"#;

fn main() {
  let mut command = Command::new("node");
  command.args(["-e", &format!("eval(atob('{}'))", BASE64_STANDARD.encode(CHILD_CODE))]);
  command.stdout(Stdio::piped());
  command.stdin(Stdio::piped());

  let mut child = command.spawn().unwrap();
  let child_stdout = child.stdout.take().unwrap();
  let mut child_stdin = child.stdin.take().unwrap();

  thread::spawn(move || {
    let mut reader = BufReader::new(child_stdout);
    loop {
      let mut buff = String::new();
      let Ok(result) = reader.read_line(&mut buff) else {
        println!("Error reading line");
        continue;
      };
      if result == 0 {
          break;
      }
      // From child
      print!("From Child: {}", String::from(buff));
    }
  });

  // To child
  let msg = r#"{ "foo": "bar" }"#;
  child_stdin.write_all(msg.as_bytes()).unwrap();

  child.wait().unwrap();
}

虽然这有效,但我想保留孩子直接输出到终端的能力,因此我正在寻找替代方法,但它也需要高性能。

最初我正在考虑让孩子与主机进程上的 http 服务器通信 - 但这会带来网络堆栈的开销,而且我还必须管理端口号并添加授权以避免拦截。

我最终希望在进程之间发送更多空间/解析/写入有效的格式(如 bson 或 protobuf),因此通信需要支持二进制数据。

它还需要在 Windows 上运行。

我不熟悉 stdio 和网络通信之外的其他类型的 IPC - 我可以获得一些有关替代方案的建议来学习吗?

rust ipc interprocess
1个回答
0
投票

套接字和命名管道是标准输入/输出流和 http(s) 之外最常用的 IPC 机制。

您需要考虑目标系统上的安全情况是否适合您,或者您是否更适合使用轻量级嵌入式 http 服务器的开销,因为如果您通过 http 进行通信,大多数环境会更宽容(s).

下面是如何实现 TCP 套接字通信的示例(如果您的一个或所有系统运行 Windows,这是一个不错的选择)。

main.rs

mod client;
mod server;

fn main() {
    // check if --server was passed on the command line
    let args: Vec<String> = std::env::args().collect();
    if args.len() > 1 && args[1] == "--server" {
        server::run().expect("Some error occurred");
        return;
    } else {
        // if not, run the client
        client::run().expect("Some error occurred");
        return;
    }
}

server.rs

use std::io::Read;
use std::net::{TcpListener, TcpStream};
use std::thread;

fn handle_client(mut stream: TcpStream) {
    let mut buffer = [0; 128];
    match stream.read(&mut buffer) {
        Ok(_) => {
            let msg = String::from_utf8_lossy(&buffer);
            println!("Received: {}", msg);
        }
        Err(e) => println!("Error: {}", e),
    }
}

pub fn run() -> std::io::Result<()> {
    let listener = TcpListener::bind("127.0.0.1:5842")?;

    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                thread::spawn(|| handle_client(stream));
            }
            Err(err) => {
                println!("Error: {}", err);
                break;
            }
        }
    }
    Ok(())
}

client.rs

use std::io::Write;
use std::net::TcpStream;

pub fn run() -> std::io::Result<()> {
    let mut stream = TcpStream::connect("127.0.0.1:5842")?;
    stream.write_all(b"Hello from client!")?;
    Ok(())
}
© www.soinside.com 2019 - 2024. All rights reserved.