Zelda

Zelda is a lightweight application-level network protocol for use in real-time applications. It is built on UDP and provides lightweight virtual connections and estimation of Round-trip time. The API is inspired by laminar.

Zelda does not provide any reliability and is instead developed to be used alongside other reliable protocols (for example TCP) or with a reliability layer on top.

Features

Quick-start

```rust use std::net::SocketAddr; use crossbeam::channel::{Sender, Receiver}; use zelda::{Socket, Config, Packet, Event};

let socket_address: SocketAddr = "127.0.0.1:38000".parse().unwrap();

let socket1 = Socket::bind(socketaddress, Config::default())?; let socket2 = Socket::bindany(Config::default())?;

println!("Address of socket 2: {}", socket2.local_address());

let packetsender: Sender = socket2.packetsender(); packetsender.send(Packet::new(socketaddress, "Hello, Client!".asbytes().tovec()));

let eventreceiver: Receiver = socket1.eventreceiver();

while let Ok(event) = event_receiver.recv() { Event::Connected(addr) => { // A connection was established with addr. }, Event::Received(addr, payload, estrtt) => { // Received payload on addr with estimated rtt. }, Event::Disconnected(addr) => { // Client with addr disconnected. break; } } ```

Another full example: ```rust let serveraddress: SocketAddr = "127.0.0.1:38000".parse().unwrap(); let clientaddress: SocketAddr = "127.0.0.1:38001".parse().unwrap();

let server = Socket::bind(serveraddress, Config::default()).unwrap(); let client = Socket::bind(clientaddress, Config::default()).unwrap();

let j1 = std::thread::spawn(move || { for _ in 0..20 { server.packetsender().send(Packet::new(clientaddress, "Hello, Client!".asbytes().tovec())).unwrap(); std::thread::sleep(std::time::Duration::frommillis(20)); } loop { match server.eventreceiver().recv() { Ok(Event::Connected(addr)) => { println!("Client connected to server!"); asserteq!(addr, clientaddress); }, Ok(Event::Received(addr, payload, rtt)) => { println!("Server received content: {}, estimated RTT: {} ms, has estimate: {}", std::str::fromutf8(&payload).unwrap(), rtt.unwrapordefault().asmillis(), rtt.issome()); asserteq!(addr, clientaddress); asserteq!("Hello, Server!".asbytes().tovec(), payload); }, Ok(Event::Disconnected(addr)) => { println!("Client disconnnected from server!"); asserteq!(addr, clientaddress); break; }, Err(err) => { panic!("Error: {}", err); } } } });

let j2 = std::thread::spawn(move || { for _ in 0..20 { client.packetsender().send(Packet::new(serveraddress, "Hello, Server!".asbytes().tovec())).unwrap(); std::thread::sleep(std::time::Duration::frommillis(20)); } loop { match client.eventreceiver().recv() { Ok(Event::Connected(addr)) => { println!("Server connected to client!"); asserteq!(addr, serveraddress); }, Ok(Event::Received(addr, payload, rtt)) => { println!("Client received content: {}, estimated RTT: {} ms, has estimate: {}", std::str::fromutf8(&payload).unwrap(), rtt.unwrapordefault().asmillis(), rtt.issome()); asserteq!(addr, serveraddress); asserteq!("Hello, Client!".asbytes().tovec(), payload); }, Ok(Event::Disconnected(addr)) => { println!("Server disconnnected from client!"); asserteq!(addr, serveraddress); break; }, Err(err) => { panic!("Error: {}", err); } } } });

j1.join().unwrap(); j2.join().unwrap(); ```

Simulating network conditions

Zelda does not include a link conditioner, instead you should use a separate program such as netem to simulate link conditions.

Using netem on linux

Setting 100ms latency with a 20ms normally distributed variation: tc qdisc change dev <interface-name> root netem delay 100ms 20ms distribution normal Check the documentation for more: netem documentation