🎓 A1Learner
🔍

Rust UDP Basics

UDP is TCP's quick, careless sibling, sending small packets called datagrams at an address. Games, voice chat and DNS choose it where speed beats perfection.

Bind a UDP socket

A socket is your end of the conversation. Port 0 takes any free port.

let socket = UdpSocket::bind("127.0.0.1:0").unwrap();

Throw a datagram at an address

send_to needs no connection — the address travels with the packet.

let sent = socket.send_to(b"ping", ("127.0.0.1", 9000)).unwrap();

Receive a packet and learn the sender

recv_from returns the byte count and the address that sent them.

let mut buf = [0u8; 64];
let (count, sender) = socket.recv_from(&mut buf).unwrap();
println!("got {} bytes from {}", count, sender.ip());

Connect a socket to one peer

After connect, send and recv work like a quiet TCP conversation — with UDP's rules.

socket.connect(("127.0.0.1", 9000)).unwrap();
socket.send(b"ping").unwrap();

Wait with a deadline

A lost packet must not hang your program. A time limit turns the wait into an Err:

socket.set_read_timeout(Some(Duration::from_secs(2))).unwrap();

Example

use std::net::UdpSocket;
use std::sync::mpsc;
use std::thread;

fn main() {
    let (tx, rx) = mpsc::channel();
    let server = thread::spawn(move || {
        let socket = UdpSocket::bind("127.0.0.1:0").unwrap();
        tx.send(socket.local_addr().unwrap()).unwrap();
        let mut buf = [0u8; 64];
        let (count, sender) = socket.recv_from(&mut buf).unwrap();
        let text = String::from_utf8_lossy(&buf[..count]).into_owned();
        socket.send_to(b"pong", sender).unwrap();
        text
    });

    let server_addr = rx.recv().unwrap();
    let client = UdpSocket::bind("127.0.0.1:0").unwrap();
    client.send_to(b"ping", server_addr).unwrap();
    let mut buf = [0u8; 64];
    let (count, from) = client.recv_from(&mut buf).unwrap();
    println!("reply = {}", String::from_utf8_lossy(&buf[..count]));
    println!("from ip = {}", from.ip());
    let received = server.join().unwrap();
    println!("server received = {received}");
}

Key points

  • UDP lives in std::net::UdpSocket.
  • Bind a socket to an address.
  • Then send_to a packet at an address.
  • No connection step exists.
  • Use recv_from to wait for one packet.
  • It returns Ok((count, sender)).
  • That holds the byte count.
  • It also holds who sent it.
  • So a server learns the client's address.
  • It learns it from the first packet.
  • Use socket.Use connect(address) when one socket talks to one peer.
  • After that, plain send and recv work.
  • Packets from anyone else are dropped.
  • The IP side is shared with TCP.
  • 127.0.0.1 means this computer.
  • The IpAddr type classifies an address.
  • It uses is_loopback, is_ipv4 and is_private.
  • Use socket.Use set_read_timeout(Some(Duration::from_secs(2))) when a reply may never come.
  • When the time runs out, recv_from returns an Err.
  • Its kind is WouldBlock.
  • It does not wait forever.
  • UDP sends and receives datagrams, and that is its whole job.

The closing program binds two sockets on the loopback address. One is a server. One is a client. The client sends one ping datagram. The server replies with pong. main prints the reply. It prints the sender's IP. It prints the server's received line, too. Expected output shown. This sandbox blocks UDP sends (EPERM). So the full run could not be completed here. The program compiles cleanly. It starts both sockets. See the verification log.

Note

A UDP packet can be lost.

It can be repeated.

It can arrive out of order.

Very large ones get cut up.

Keep datagrams small.

A few kilobytes is small enough.

Design for loss.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

What is the key difference between UDP and TCP in std::net?