Provides simple cross-platform generic IPC message passing built on top of the interprocess
crate.
Add this to your Cargo.toml
:
[dependencies]
ipc_util = "0.1"
There are three functions that can be used to send messages to an IPC server as a client:
- The
send_ipc_message
function connects to the socket and sends an arbitrary serializable object over it. - The
send_ipc_query
function connects to the socket, sends an arbitrary serializable object, and reads an arbitrary deserializable object in response.
There are two functions that can be used to spawn an IPC server thread:
- The
start_ipc_listener
function is used to spawn an IPC server thread using a callback that is passed aLocalSocketStream
directly, as can be seen in the stream example. - The
start_ipc_server
function is a wrapper aroundstart_ipc_listener
, where the callback instead receives an arbitrary serializable objectTRequest
and returns anOption<TResponse>
. When a response is returned, it is sent back to the client. This can be seen in the server example.
It's recommended to use start_ipc_server
and its connection callback's Option
return type, where returning a Some
variant matches up with a send_ipc_query
call from the client, and returning None
matches up with send_ipc_message
.
use interprocess::local_socket::NameTypeSupport;
use ipc_util::{send_ipc_message, send_ipc_query, start_ipc_server};
use serde::{Deserialize, Serialize};
pub const MY_SOCKET_PATH: &str = "/tmp/ipc-util-ex-server.sock";
pub const MY_SOCKET_NAMESPACE: &str = "@ipc-util-ex-server.sock";
pub fn get_ipc_name() -> &'static str {
use NameTypeSupport::*;
match NameTypeSupport::query() {
OnlyPaths => MY_SOCKET_PATH,
OnlyNamespaced | Both => MY_SOCKET_NAMESPACE,
}
}
#[derive(Debug, Serialize, Deserialize)]
pub enum Message {
Text { text: String },
Ping,
Pong,
}
fn run_server() {
start_ipc_server(
get_ipc_name(),
|message: Message| match message {
Message::Text { text } => {
println!("{text}");
None
}
Message::Ping => Some(Message::Pong),
_ => None,
},
Some(|e| panic!("Incoming connection error: {e}")),
)
.expect("Failed to start ipc listener")
.join()
.expect("Failed to join server thread");
}
fn run_client() {
let text = Message::Text {
text: "Hello from client!".to_string(),
};
let ping = Message::Ping;
send_ipc_message(get_ipc_name(), &text).expect("Failed to connect to socket");
let response: Message =
send_ipc_query(get_ipc_name(), &ping).expect("Failed to connect to socket");
dbg!(response);
}
fn main() {
let args = std::env::args().collect::<Vec<_>>();
match args.get(1).map(|s| s.as_str()) {
Some("server") => run_server(),
Some("client") => run_client(),
_ => {
println!("Usage: {} [server|client]", args[0]);
std::process::exit(1);
}
}
}
You can run the examples with cargo run --example server server
and cargo run --example server client
.