Skip to Content
Course content
Click on the "Edit" button in the top corner of the screen to edit your slide content.

Up until now, we've mostly dealt with TCP or local logic. But sometimes, the overhead of a guaranteed connection is just too much. If you're building a game, a streaming service, or a simple discovery tool, you want UDP. It's "fire and forget"—you throw a packet at an IP and port, and you hope for the best. No handshakes, no sequence numbers, just raw speed.

To show you how this works, let's build a simple "Heartbeat Monitor." We'll create a server that listens for "ping" messages from a client and replies with a "pong." It's a classic pattern for checking if a remote machine is still alive without maintaining a heavy persistent connection.

Binding to a Local Port

First, we need the server to claim a spot on the network. In Rust, we use UdpSocket::bind. Unlike TCP, where you bind and then listen() for incoming connections, a UDP socket is ready to receive data the moment it's bound.

use std::net::UdpSocket;

fn main() -> std::io::Result<()> {
    // I'm using 127.0.0.1 for local testing. 
    // Port 34253 is just a random high port I picked to avoid conflicts.
    let socket = UdpSocket::bind("127.0.0.1:34253")?;
    println!("Listening on 127.0.0.1:34253...");
    
    Ok(())
}

The Buffer Trap

Now we need to actually wait for data. recv_from is the tool here. It takes a mutable slice (the buffer) and returns how many bytes were actually written into that buffer, along with the address of the sender.

I'll be honest: every time I start a new UDP project, I almost make the same mistake with the buffer. Watch what happens when I try to print the received message:

let mut buf = [0u8; 1024]; // A 1KB buffer
let (amt, src) = socket.recv_from(&mut buf)?;

// MISTAKE: Printing the whole buffer
println!("Received: {:?}", buf); 

If the client sends the word "Ping" (4 bytes), the code above will print "Ping" followed by 1,020 zeros. It's a common annoyance. We have to use the amt variable—the number of bytes actually received—to slice the buffer correctly. Let's fix that:

let (amt, src) = socket.recv_from(&mut buf)?;
let filled_buf = &buf[..amt]; 
println!("Received {} bytes from {}: {:?}", amt, src, filled_buf);

Sending the Response

Since UDP is connectionless, the socket doesn't "know" who it's talking to. That's why recv_from gives us the src address. If we want to reply, we use send_to, passing in the data and that specific address.

Here is the complete server logic. I'm wrapping it in a loop because we want this monitor to run indefinitely.

use std::net::UdpSocket;

fn main() -> std::io::Result<()> {
    let socket = UdpSocket::bind("127.0.0.1:34253")?;
    let mut buf = [0u8; 1024];

    loop {
        let (amt, src) = socket.recv_from(&mut buf)?;
        let msg = std::str::from_utf8(&buf[..amt]).unwrap_or("Invalid UTF-8");
        
        println!("Heartbeat from {}: {}", src, msg);

        if msg == "ping" {
            socket.send_to(b"pong", &src)?;
        }
    }
}

Wiring up the Client

The client is even simpler. Interestingly, a UdpSocket on the client side still needs to be "bound," but since we don't care which port the OS assigns to us for sending, we bind to port 0. This tells the system, "just give me any available port."

use std::net::UdpSocket;

fn main() -> std::io::Result<()> {
    // Bind to 0 to let the OS choose a random available port
    let socket = UdpSocket::bind("127.0.0.1:0")?;
    
    // We don't "connect", we just send to the destination
    socket.send_to(b"ping", "127.0.0.1:34253")?;
    println!("Ping sent!");

    let mut buf = [0u8; 1024];
    let (amt, src) = socket.recv_from(&mut buf)?;
    println!("Response from {}: {:?}", src, &buf[..amt]);

    Ok(())
}

When you run the server first and then the client, you'll see the handshake happen. Remember: if the server isn't running, the client's send_to will still succeed. That's the nature of UDP—it doesn't check if the destination is actually listening. It just launches the packet into the void.




📋 Practical Task

Build a UDP-based System Status Broadcaster

Your task is to create a small system that simulates a sensor reporting its status to a monitoring server. Instead of a simple "ping/pong," implement the following:

  • The Broadcaster (Client): Create a loop that sends a string in the format "STATUS: OK" or "STATUS: ERROR" every 2 seconds to 127.0.0.1:4000.
  • The Monitor (Server): Bind to 127.0.0.1:4000. It should receive the message and, if it sees "STATUS: ERROR", print a loud warning to the console: "!!! ALERT: System Failure Detected from [Source Address] !!!". Otherwise, it should just print "System Healthy".

Make sure you handle the buffer slicing correctly so you aren't comparing a 1024-byte array against a short string!

Rating
0 0

There are no comments for now.

to be the first to leave a comment.