#![cfg_attr(
target_family = "unix",
expect(
clippy::unwrap_used,
clippy::expect_used,
reason = "example: panic-on-error is the standard pattern for demos"
)
)]
#[cfg(target_family = "unix")]
mod unix_example {
use rama::{
bytes::Bytes,
futures::{FutureExt, SinkExt, StreamExt},
stream::codec::BytesCodec,
telemetry::tracing::{
self,
level_filters::LevelFilter,
subscriber::{EnvFilter, fmt, layer::SubscriberExt, util::SubscriberInitExt},
},
unix::{UnixDatagram, UnixDatagramFramed, UnixSocketAddress},
utils::time::now_unix_ms,
};
use std::{io, process, time::Duration};
use tokio::time;
pub(super) async fn run() {
tracing::subscriber::registry()
.with(fmt::layer())
.with(
EnvFilter::builder()
.with_default_directive(LevelFilter::DEBUG.into())
.from_env_lossy(),
)
.init();
let path_a = random_tmp_path("rama_example_unix_datagram_a");
let path_b = random_tmp_path("rama_example_unix_datagram_b");
let socket_a = UnixDatagram::bind(&path_a).expect("bind unix datagram A");
let mut socket_framed_a = UnixDatagramFramed::new(socket_a, BytesCodec::new());
tracing::info!(
file.path = %path_a,
"unix Datagram socket A ready for action",
);
let socket_b = UnixDatagram::bind(&path_b).expect("bind unix datagram B");
let mut socket_framed_b = UnixDatagramFramed::new(socket_b, BytesCodec::new());
tracing::info!(
file.path = %path_b,
"unix Datagram socket B ready for action",
);
let b_addr: UnixSocketAddress = socket_framed_b
.get_ref()
.local_addr()
.expect("get local addr")
.into();
let a = ping(&mut socket_framed_a, b_addr);
let b = pong(&mut socket_framed_b);
if let Err(e) = tokio::try_join!(a, b) {
tracing::error!("an error occurred; error = {e:?}");
} else {
tracing::info!("done!");
}
std::fs::remove_file(path_a).expect("delete tmp a");
std::fs::remove_file(path_b).expect("delete tmp b");
}
async fn ping(
socket: &mut UnixDatagramFramed<BytesCodec>,
b_addr: UnixSocketAddress,
) -> Result<(), io::Error> {
socket.send((Bytes::from(&b"PING"[..]), b_addr)).await?;
for _ in 0..4 {
let (bytes, addr) = socket.next().map(|e| e.unwrap()).await?;
tracing::info!("[a] recv: {}", String::from_utf8_lossy(&bytes));
socket.send((Bytes::from(&b"PING"[..]), addr)).await?;
}
Ok(())
}
async fn pong(socket: &mut UnixDatagramFramed<BytesCodec>) -> Result<(), io::Error> {
let timeout = Duration::from_millis(200);
while let Ok(Some(Ok((bytes, addr)))) = time::timeout(timeout, socket.next()).await {
tracing::info!("[b] recv: {}", String::from_utf8_lossy(&bytes));
socket.send((Bytes::from(&b"PONG"[..]), addr)).await?;
}
Ok(())
}
fn random_tmp_path(prefix: &str) -> String {
let pid = process::id();
format!("/tmp/{prefix}_{pid}_{}", now_unix_ms())
}
}
#[cfg(target_family = "unix")]
use unix_example::run;
#[cfg(not(target_family = "unix"))]
async fn run() {
eprintln!("unix_datagram socket example is a unix-only example, bye now!");
}
#[tokio::main]
async fn main() {
run().await
}