我正在生成一个子进程,我控制其内部代码,并且需要子进程和主机进程之间的双向通信。
现在我有一个简单的实现,让主机通过 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 - 我可以获得一些有关替代方案的建议来学习吗?
套接字和命名管道是标准输入/输出流和 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(())
}