use turn::client::*;
use anyhow::Result;
use clap::{App, AppSettings, Arg};
use std::sync::Arc;
use tokio::net::UdpSocket;
use tokio::time::Duration;
use util::Conn;
#[tokio::main]
async fn main() -> Result<()> {
env_logger::init();
let mut app = App::new("TURN Client UDP")
.version("0.1.0")
.author("Rain Liu <yliu@webrtc.rs>")
.about("An example of TURN Client UDP")
.setting(AppSettings::DeriveDisplayOrder)
.setting(AppSettings::SubcommandsNegateReqs)
.arg(
Arg::with_name("FULLHELP")
.help("Prints more detailed help information")
.long("fullhelp"),
)
.arg(
Arg::with_name("host")
.required_unless("FULLHELP")
.takes_value(true)
.long("host")
.help("TURN Server name."),
)
.arg(
Arg::with_name("user")
.required_unless("FULLHELP")
.takes_value(true)
.long("user")
.help("A pair of username and password (e.g. \"user=pass\")"),
)
.arg(
Arg::with_name("realm")
.default_value("webrtc.rs")
.takes_value(true)
.long("realm")
.help("Realm (defaults to \"webrtc.rs\")"),
)
.arg(
Arg::with_name("port")
.takes_value(true)
.default_value("3478")
.long("port")
.help("Listening port."),
)
.arg(
Arg::with_name("ping")
.long("ping")
.takes_value(false)
.help("Run ping test"),
);
let matches = app.clone().get_matches();
if matches.is_present("FULLHELP") {
app.print_long_help().unwrap();
std::process::exit(0);
}
let host = matches.value_of("host").unwrap();
let port = matches.value_of("port").unwrap();
let user = matches.value_of("user").unwrap();
let cred: Vec<&str> = user.splitn(2, "=").collect();
let ping = matches.is_present("ping");
let realm = matches.value_of("realm").unwrap();
let conn = UdpSocket::bind("0.0.0.0:0").await?;
let turn_server_addr = format!("{}:{}", host, port);
let cfg = ClientConfig {
stun_serv_addr: turn_server_addr.clone(),
turn_serv_addr: turn_server_addr,
username: cred[0].to_string(),
password: cred[1].to_string(),
realm: realm.to_string(),
software: String::new(),
rto_in_ms: 0,
conn: Arc::new(conn),
vnet: None,
};
let client = Client::new(cfg).await?;
client.listen().await?;
let relay_conn = client.allocate().await?;
println!(
"relayed-address={}",
relay_conn.local_addr().await?.to_string()
);
if ping {
do_ping_test(&client, relay_conn).await?;
}
client.close().await?;
Ok(())
}
async fn do_ping_test(
client: &Client,
relay_conn: impl Conn + std::marker::Send + std::marker::Sync + 'static,
) -> Result<()> {
let mapped_addr = client.send_binding_request().await?;
let pinger_conn_tx = Arc::new(UdpSocket::bind("0.0.0.0:0").await?);
relay_conn.send_to("Hello".as_bytes(), mapped_addr).await?;
let relay_addr = relay_conn.local_addr().await?;
let pinger_conn_rx = Arc::clone(&pinger_conn_tx);
tokio::spawn(async move {
let mut buf = vec![0u8; 1500];
loop {
let (n, from) = match pinger_conn_rx.recv_from(&mut buf).await {
Ok((n, from)) => (n, from),
Err(_) => break,
};
let msg = match String::from_utf8(buf[..n].to_vec()) {
Ok(msg) => msg,
Err(_) => break,
};
println!("pingerConn read-loop: {} from {}", msg, from);
}
});
tokio::spawn(async move {
let mut buf = vec![0u8; 1500];
loop {
let (n, from) = match relay_conn.recv_from(&mut buf).await {
Err(_) => break,
Ok((n, from)) => (n, from),
};
println!("relay_conn read-loop: {:?} from {}", &buf[..n], from);
if relay_conn.send_to(&buf[..n], from).await.is_err() {
break;
}
}
});
tokio::time::sleep(Duration::from_millis(500)).await;
for _ in 0..2 {
let msg = "12345678910".to_owned(); println!("sending msg={} with size={}", msg, msg.as_bytes().len());
pinger_conn_tx.send_to(msg.as_bytes(), relay_addr).await?;
tokio::time::sleep(Duration::from_secs(1)).await;
}
Ok(())
}