//! Implementation of a simple uTP client and server.
#[macro_use]
extern crate log;
extern crate env_logger;
extern crate utp;
use std::process;
fn usage() -> ! {
println!("Usage: utp [-s|-c]
");
process::exit(1);
}
fn main() {
use utp::UtpStream;
use std::io::{stdin, stdout, stderr, Read, Write};
// This example may run in either server or client mode.
// Using an enum tends to make the code cleaner and easier to read.
enum Mode {Server, Client}
// Start logging
env_logger::init().expect("Error starting logger");
// Fetch arguments
let mut args = std::env::args();
// Skip program name
args.next();
// Parse the mode argument
let mode: Mode = match args.next() {
Some(ref s) if s == "-s" => Mode::Server,
Some(ref s) if s == "-c" => Mode::Client,
_ => usage(),
};
// Parse the address argument or use a default if none is provided
let addr = match (args.next(), args.next()) {
(None, None) => "127.0.0.1:8080".to_owned(),
(Some(ip), Some(port)) => format!("{}:{}", ip, port),
_ => usage(),
};
let addr: &str = &addr;
match mode {
Mode::Server => {
// Create a listening stream
let mut stream = UtpStream::bind(addr).expect("Error binding stream");
let mut writer = stdout();
let _ = writeln!(&mut stderr(), "Serving on {}", addr);
// Create a reasonably sized buffer
let mut payload = vec![0; 1024 * 1024];
// Wait for a new connection and print the received data to stdout.
// Reading and printing chunks like this feels more interactive than trying to read
// everything with `read_to_end` and avoids resizing the buffer multiple times.
loop {
match stream.read(&mut payload) {
Ok(0) => break,
Ok(read) => writer.write(&payload[..read]).expect("Error writing to stdout"),
Err(e) => panic!("{}", e)
};
}
}
Mode::Client => {
// Create a stream and try to connect to the remote address
let mut stream = UtpStream::connect(addr).expect("Error connecting to remote peer");
let mut reader = stdin();
// Create a reasonably sized buffer
let mut payload = vec![0; 1024 * 1024];
// Read from stdin and send it to the remote server.
// Once again, reading and sending small chunks like this avoids having to read the
// entire input (which may be endless!) before starting to send, unlike what would
// happen if we were to use `read_to_end` on `reader`.
loop {
match reader.read(&mut payload) {
Ok(0) => break,
Ok(read) => stream.write(&payload[..read]).expect("Error writing to stream"),
Err(e) => {
stream.close().expect("Error closing stream");
panic!("{:?}", e);
}
};
}
// Explicitly close the stream.
stream.close().expect("Error closing stream");
}
}
}