use std::time::Duration;
use tokio::time::sleep;
use tokio::net::UdpSocket;
use tracing::{info, debug, warn, error};
use rvoip_rtp_core::transport::{PlatformType, PlatformSocketStrategy, RtpSocketValidator};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
let platform = PlatformType::current();
info!("Running on platform: {:?}", platform);
let default_strategy = PlatformSocketStrategy::for_current_platform();
info!("Default socket strategy for this platform: {:#?}", default_strategy);
match platform {
PlatformType::Windows => {
info!("Windows socket notes:");
info!("- Windows uses Winsock API rather than Berkeley sockets");
info!("- SO_REUSEADDR behaves differently than on Unix systems");
info!("- Windows has no direct equivalent to Unix's SO_REUSEPORT");
},
PlatformType::MacOS => {
info!("macOS socket notes:");
info!("- Typically requires both SO_REUSEADDR AND SO_REUSEPORT");
info!("- Has more BSD-like socket behavior");
info!("- IPV6_V6ONLY defaults to ON (IPv6 sockets don't handle IPv4)");
},
PlatformType::Linux => {
info!("Linux socket notes:");
info!("- Usually SO_REUSEADDR alone is sufficient");
info!("- SO_REUSEPORT has different semantics (load balancing)");
info!("- Older kernels default IPV6_V6ONLY to OFF");
},
PlatformType::Other => {
info!("Running on an unsupported platform");
info!("Will use generic socket settings");
}
}
info!("\nRunning socket validation tests...");
let validation_result = RtpSocketValidator::validate().await;
match &validation_result {
Ok(ref strategy) => {
info!("Socket validation succeeded!");
info!("Recommended socket strategy: {:#?}", strategy);
if strategy.use_reuse_addr != default_strategy.use_reuse_addr {
info!("Use SO_REUSEADDR changed from {} to {}",
default_strategy.use_reuse_addr, strategy.use_reuse_addr);
}
if strategy.use_reuse_port != default_strategy.use_reuse_port {
info!("Use SO_REUSEPORT changed from {} to {}",
default_strategy.use_reuse_port, strategy.use_reuse_port);
}
if strategy.buffer_size != default_strategy.buffer_size {
info!("Buffer size changed from {} to {} bytes",
default_strategy.buffer_size, strategy.buffer_size);
}
if strategy.rebind_wait_time_ms != default_strategy.rebind_wait_time_ms {
info!("Rebind wait time changed from {} to {} ms",
default_strategy.rebind_wait_time_ms, strategy.rebind_wait_time_ms);
}
},
Err(ref e) => {
error!("Socket validation failed: {}", e);
error!("Using default settings may result in issues on this platform");
}
}
info!("\nDemonstrating socket creation with platform-specific settings...");
let strategy = match &validation_result {
Ok(s) => s.clone(),
Err(_) => PlatformSocketStrategy::for_current_platform(),
};
let socket1 = UdpSocket::bind("127.0.0.1:0").await?;
strategy.apply_to_socket(&socket1).await?;
let addr1 = socket1.local_addr()?;
let socket2 = UdpSocket::bind("127.0.0.1:0").await?;
strategy.apply_to_socket(&socket2).await?;
let addr2 = socket2.local_addr()?;
info!("Successfully created two sockets with platform-specific settings:");
info!("Socket 1: {}", addr1);
info!("Socket 2: {}", addr2);
info!("\nSending test data between sockets...");
let test_data = b"Cross-platform socket test";
socket1.send_to(test_data, addr2).await?;
let mut buf = vec![0u8; 1024];
let (len, from) = socket2.recv_from(&mut buf).await?;
info!("Socket 2 received {} bytes from {}", len, from);
if &buf[..len] == test_data {
info!("Data integrity check passed!");
} else {
error!("Data integrity check failed!");
}
let response = b"Response received";
socket2.send_to(response, addr1).await?;
let (len, from) = socket1.recv_from(&mut buf).await?;
info!("Socket 1 received {} bytes from {}", len, from);
if &buf[..len] == response {
info!("Response data integrity check passed!");
} else {
error!("Response data integrity check failed!");
}
info!("\nDemonstrating socket rebinding (requiring SO_REUSEADDR)...");
drop(socket1);
info!("Waiting {}ms before rebinding...", strategy.rebind_wait_time_ms);
sleep(Duration::from_millis(strategy.rebind_wait_time_ms)).await;
let socket3 = UdpSocket::bind(addr1).await?;
strategy.apply_to_socket(&socket3).await?;
info!("Successfully rebound to port: {}", addr1.port());
let rebind_data = b"Socket rebind test successful";
socket3.send_to(rebind_data, addr2).await?;
let (len, from) = socket2.recv_from(&mut buf).await?;
info!("Socket 2 received {} bytes from rebinded socket", len);
if &buf[..len] == rebind_data {
info!("Rebind data integrity check passed!");
} else {
error!("Rebind data integrity check failed!");
}
info!("\nCross-platform socket validation test completed successfully!");
Ok(())
}