This commit is contained in:
parent
69be9f7aca
commit
089def45d1
37 changed files with 2792 additions and 225 deletions
|
|
@ -1,5 +1,6 @@
|
|||
mod auth;
|
||||
mod messages;
|
||||
mod pipes;
|
||||
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
|
@ -38,6 +39,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
let (conn, keyring) = auth::connect_or_register(config, host_public_key, "client").await?;
|
||||
messages::send_and_receive(&conn, &keyring, &server_bundle).await?;
|
||||
|
||||
println!("\n--- Pipe demo ---");
|
||||
pipes::run_pipe_demo(&conn, 1).await?;
|
||||
|
||||
conn.sender.close();
|
||||
println!("\nDone");
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,13 +96,12 @@ pub async fn send_and_receive(
|
|||
println!("Sending: {msg}");
|
||||
conn.sender.send(&msg).await?;
|
||||
|
||||
match conn.receiver.receive().await {
|
||||
match conn.receive().await {
|
||||
Ok(resp) => {
|
||||
println!("Received: {resp}");
|
||||
}
|
||||
Err(e) => eprintln!("Receive error: {e}"),
|
||||
}
|
||||
|
||||
conn.sender.close();
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
137
example/client/src/pipes.rs
Normal file
137
example/client/src/pipes.rs
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
use mtp::client::MTPConnection;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::sync::oneshot;
|
||||
use tokio::time::{Duration, Instant};
|
||||
|
||||
pub async fn run_pipe_demo(
|
||||
conn: &MTPConnection,
|
||||
iterations: usize,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let sizes = [64, 256, 1024, 4096];
|
||||
let mut all_elapsed = Vec::with_capacity(sizes.len() * iterations);
|
||||
let mut all_data_only = Vec::with_capacity(sizes.len() * iterations);
|
||||
|
||||
for (i, &size) in sizes.iter().enumerate() {
|
||||
let mut size_elapsed = Vec::with_capacity(iterations);
|
||||
let mut size_data_only = Vec::with_capacity(iterations);
|
||||
|
||||
for run in 0..iterations {
|
||||
let random_bytes: Vec<u8> = (0..size).map(|_| rand::random::<u8>()).collect();
|
||||
let description = format!("pipe-demo-{i}-run{run}");
|
||||
println!(" [pipe {i}.{run}] creating pipe ({size} bytes): {description}");
|
||||
|
||||
let handle = conn.create_pipe(&description).await?;
|
||||
let pipe_id = handle.pipe_id();
|
||||
println!(" [pipe {i}.{run}] create_pipe returned (pipe_id={pipe_id})");
|
||||
|
||||
// Overall timer starts before any I/O
|
||||
let overall_start = Instant::now();
|
||||
|
||||
// Channel to capture the instant the writer actually starts writing
|
||||
let (write_start_tx, write_start_rx) = oneshot::channel();
|
||||
|
||||
let write_bytes = random_bytes.clone();
|
||||
let writer_handle = tokio::spawn(async move {
|
||||
println!(" [pipe {i}.{run}] writer: waiting for server accept ...");
|
||||
match handle.wait().await {
|
||||
Ok(Some(mut writer)) => {
|
||||
// Record the instant we begin writing
|
||||
let _ = write_start_tx.send(Instant::now());
|
||||
|
||||
println!(
|
||||
" [pipe {i}.{run}] writer: pipe accepted (pipe_id={pipe_id}), writing {} bytes ...",
|
||||
write_bytes.len()
|
||||
);
|
||||
writer
|
||||
.write_all(&write_bytes)
|
||||
.await
|
||||
.map_err(|e| mtp::common::PipeError::IoError(e.to_string()))?;
|
||||
writer
|
||||
.finish()
|
||||
.await
|
||||
.map_err(|e| mtp::common::PipeError::IoError(e.to_string()))?;
|
||||
println!(" [pipe {i}.{run}] writer: data sent and finished");
|
||||
Ok::<(), mtp::common::PipeError>(())
|
||||
}
|
||||
Ok(None) => {
|
||||
eprintln!(" [pipe {i}.{run}] writer: pipe denied by server");
|
||||
Err(mtp::common::PipeError::Rejected)
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!(" [pipe {i}.{run}] writer: error: {e}");
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
println!(" [pipe {i}.{run}] waiting for server's return pipe via receive_pipe() ...");
|
||||
let pipe_req = conn.receive_pipe().await?;
|
||||
println!(
|
||||
" [pipe {i}.{run}] received return pipe: id={} desc={:?}",
|
||||
pipe_req.id(),
|
||||
pipe_req.description()
|
||||
);
|
||||
|
||||
let mut reader = pipe_req.accept().await?;
|
||||
println!(" [pipe {i}.{run}] return pipe accepted, reading data ...");
|
||||
|
||||
let mut buf = Vec::new();
|
||||
reader.read_to_end(&mut buf).await?;
|
||||
let overall_elapsed = overall_start.elapsed();
|
||||
|
||||
// Receive the instant the writer started writing
|
||||
let data_start = write_start_rx.await?;
|
||||
let data_only_elapsed = Instant::now() - data_start;
|
||||
|
||||
match writer_handle.await {
|
||||
Ok(Ok(())) => {}
|
||||
Ok(Err(e)) => eprintln!(" [pipe {i}.{run}] writer error: {e}"),
|
||||
Err(e) => eprintln!(" [pipe {i}.{run}] writer task panicked: {e}"),
|
||||
}
|
||||
|
||||
let matches = buf == random_bytes;
|
||||
println!(
|
||||
" [pipe {i}.{run}] round-trip: {} bytes, \
|
||||
total={:.3}ms, data-only={:.3}ms, match={matches}",
|
||||
size,
|
||||
overall_elapsed.as_secs_f64() * 1000.0,
|
||||
data_only_elapsed.as_secs_f64() * 1000.0,
|
||||
);
|
||||
|
||||
size_elapsed.push(overall_elapsed);
|
||||
size_data_only.push(data_only_elapsed);
|
||||
all_elapsed.push(overall_elapsed);
|
||||
all_data_only.push(data_only_elapsed);
|
||||
}
|
||||
|
||||
// ---- per-size averages ----
|
||||
let avg_total = average_duration(&size_elapsed);
|
||||
let avg_data = average_duration(&size_data_only);
|
||||
println!(
|
||||
" [pipe {i}] AVERAGE for size {size}: \
|
||||
total={avg_total:.3}ms, data-only={avg_data:.3}ms \
|
||||
(over {iterations} runs)"
|
||||
);
|
||||
}
|
||||
|
||||
// ---- overall averages ----
|
||||
let overall_total = average_duration(&all_elapsed);
|
||||
let overall_data = average_duration(&all_data_only);
|
||||
println!(
|
||||
" [summary] OVERALL AVERAGE loopback time: \
|
||||
total={overall_total:.3}ms, data-only={overall_data:.3}ms \
|
||||
({} measurements)",
|
||||
all_elapsed.len()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Helper: average a slice of Durations without overflowing.
|
||||
fn average_duration(durations: &[Duration]) -> f64 {
|
||||
if durations.is_empty() {
|
||||
return 0.0;
|
||||
}
|
||||
let sum_ms: f64 = durations.iter().map(|d| d.as_secs_f64() * 1000.0).sum();
|
||||
sum_ms / durations.len() as f64
|
||||
}
|
||||
Loading…
Reference in a new issue