如何使用 Unix 套接字对通信 Rust 和 Ruby 进程
How to communicate a Rust and a Ruby process using a Unix socket pair
我正在尝试使用 Unix 套接字对将 Rust 进程与子 Ruby 进程进行通信。我只使用 Ruby 尝试过同样的方法并且它有效,但我似乎无法让它与 Rust 一起工作。
我试过将 "rust_socket" 文件描述符传递给 Ruby 脚本,将 "ruby_socket" 文件描述符传递给 Ruby 以及读/写的不同组合插座。我觉得我应该传递 "ruby_socket" 文件描述符,但是当我这样做时,我得到了一个错误的文件描述符错误。
// The rust side of things
use std::process::Command;
use std::os::unix::net::UnixStream;
use std::os::unix::io::IntoRawFd;
use std::io::{Read, Write};
fn main() {
let (rust_socket, mut ruby_socket) = match UnixStream::pair() {
Ok((rust_socket, ruby_socket)) => (rust_socket, ruby_socket),
Err(e) => {
println!("Failed to open socket pair: {:?}", e);
return;
}
};
let _output = Command::new("ruby")
.args(&["/home/station/workspace/rust_server/src/client.rb", &rust_socket.into_raw_fd().to_string()])
.spawn()
.expect("Failed to start ruby process");
let mut response = String::new();
ruby_socket.read_to_string(&mut response).unwrap();
}
# The ruby side of things
require "socket"
begin
socket = UNIXSocket.for_fd(ARGV.shift.to_i)
socket.send("Hello world!\n", 0)
ensure
socket&.close
end
我希望能够从 Rust 读取 "Hello world!" 字符串,但它不起作用。
问题似乎是Rust sets all file descriptors to be closed when a child is created by setting the FD_CLOEXEC
flag。解决这个问题的唯一方法似乎是使用 libc 调用 fcntl
.
这里有一些代码似乎可以工作,但我不了解 Rust,所以使用风险自负。您将遇到的另一个问题是您需要在生成 child 之后关闭 rust_socket
的 parent 端,否则 read_to_string
将永远阻塞,等待流被关闭。您可以使用 drop
执行此操作,但您还需要使用 AsRawFd
而不是 IntoRawFd
:
use std::process::Command;
use std::os::unix::net::UnixStream;
use std::os::unix::io::AsRawFd;
use std::io::Read;
extern crate libc;
fn main() {
// Create the socket pair.
let (rust_socket, mut ruby_socket) = UnixStream::pair().unwrap();
// Unset FD_CLOEXEC on the socket to be passed to the child.
let fd = rust_socket.as_raw_fd();
unsafe {
let flags = libc::fcntl(fd, libc::F_GETFD);
libc::fcntl(fd, libc::F_SETFD, flags & !libc::FD_CLOEXEC);
}
// Spawn the child
let _output = Command::new("ruby")
.args(&["client.rb", &fd.to_string()])
.spawn();
// After spawning, close the parents side of rust_socket.
// If we use IntoRawFd, rust_socket would have been moved by this point
// so we need AsRawFD instead.
drop(rust_socket);
let mut response = String::new();
ruby_socket.read_to_string(&mut response).unwrap();
println!("Ruby said '{}'", response);
}
我正在尝试使用 Unix 套接字对将 Rust 进程与子 Ruby 进程进行通信。我只使用 Ruby 尝试过同样的方法并且它有效,但我似乎无法让它与 Rust 一起工作。
我试过将 "rust_socket" 文件描述符传递给 Ruby 脚本,将 "ruby_socket" 文件描述符传递给 Ruby 以及读/写的不同组合插座。我觉得我应该传递 "ruby_socket" 文件描述符,但是当我这样做时,我得到了一个错误的文件描述符错误。
// The rust side of things
use std::process::Command;
use std::os::unix::net::UnixStream;
use std::os::unix::io::IntoRawFd;
use std::io::{Read, Write};
fn main() {
let (rust_socket, mut ruby_socket) = match UnixStream::pair() {
Ok((rust_socket, ruby_socket)) => (rust_socket, ruby_socket),
Err(e) => {
println!("Failed to open socket pair: {:?}", e);
return;
}
};
let _output = Command::new("ruby")
.args(&["/home/station/workspace/rust_server/src/client.rb", &rust_socket.into_raw_fd().to_string()])
.spawn()
.expect("Failed to start ruby process");
let mut response = String::new();
ruby_socket.read_to_string(&mut response).unwrap();
}
# The ruby side of things
require "socket"
begin
socket = UNIXSocket.for_fd(ARGV.shift.to_i)
socket.send("Hello world!\n", 0)
ensure
socket&.close
end
我希望能够从 Rust 读取 "Hello world!" 字符串,但它不起作用。
问题似乎是Rust sets all file descriptors to be closed when a child is created by setting the FD_CLOEXEC
flag。解决这个问题的唯一方法似乎是使用 libc 调用 fcntl
.
这里有一些代码似乎可以工作,但我不了解 Rust,所以使用风险自负。您将遇到的另一个问题是您需要在生成 child 之后关闭 rust_socket
的 parent 端,否则 read_to_string
将永远阻塞,等待流被关闭。您可以使用 drop
执行此操作,但您还需要使用 AsRawFd
而不是 IntoRawFd
:
use std::process::Command;
use std::os::unix::net::UnixStream;
use std::os::unix::io::AsRawFd;
use std::io::Read;
extern crate libc;
fn main() {
// Create the socket pair.
let (rust_socket, mut ruby_socket) = UnixStream::pair().unwrap();
// Unset FD_CLOEXEC on the socket to be passed to the child.
let fd = rust_socket.as_raw_fd();
unsafe {
let flags = libc::fcntl(fd, libc::F_GETFD);
libc::fcntl(fd, libc::F_SETFD, flags & !libc::FD_CLOEXEC);
}
// Spawn the child
let _output = Command::new("ruby")
.args(&["client.rb", &fd.to_string()])
.spawn();
// After spawning, close the parents side of rust_socket.
// If we use IntoRawFd, rust_socket would have been moved by this point
// so we need AsRawFD instead.
drop(rust_socket);
let mut response = String::new();
ruby_socket.read_to_string(&mut response).unwrap();
println!("Ruby said '{}'", response);
}