如何使用Unix套接字对传递Rust和Ruby进程

时间:2019-04-05 17:23:43

标签: ruby sockets rust unix-socket

我正在尝试使用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

我希望能够阅读“ Hello world!” Rust中的字符串,但不起作用。

1 个答案:

答案 0 :(得分:0)

问题似乎出在Rust sets all file descriptors to be closed when a child is created by setting the FD_CLOEXEC flag上。解决此问题的唯一方法似乎是使用libc调用fcntl

有些代码似乎有效,但是我不知道Rust,因此使用风险自负。您将遇到的另一个问题是,在给孩子撒布后需要关闭rust_socket的父侧,否则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);
}