How to receive udp messages in a nonblocking fashion

What it says in the title.
My code:

const std = @import("std");

pub fn main() !void {
    const addr = try std.net.Address.parseIp("127.0.0.1", 32100);

    const sock = try std.posix.socket(
        std.posix.AF.INET,
        std.posix.SOCK.DGRAM | std.posix.SOCK.NONBLOCK,
        std.posix.IPPROTO.UDP,
    );
    defer std.posix.close(sock);
    try std.posix.bind(sock, &addr.any, addr.getOsSockLen());
    std.log.info("Listen on {any}...", .{addr});

    while (true) {
        var buf: [1024]u8 = undefined;
        var other_addr: std.posix.sockaddr = undefined;
        var other_addrlen: std.posix.socklen_t = @sizeOf(std.posix.sockaddr);

        // i need to check if there is something in the socket here, if not then it will return an error, that being "wouldblock"
        // i have tried to use std.io.poll, but that empties the socket into its fifo thingy which then prevents me from obtaining the other_addr with its information, which i want too
        // my last options would be to just catch the wouldblock error from revcfrom and do nothing in that case, but it feels like it might be a code smell

        const in = buf[0..try std.posix.recvfrom(sock, &buf, 0, &other_addr, &other_addrlen)];
        std.log.info("{d} sent {s}", .{ other_addr.data[2..6], in });

        const n_sent = try std.posix.sendto(sock, in, 0, &other_addr, other_addrlen);
        std.log.info("echoed {d} byte(s) back", .{n_sent});
    }
}

The code was originally copy-pasted from UDP Echo - Zig cookbook

If there are any other issues that I just dont see here, please let me know. Im not too familiar with the linux apis

Update: The error handling method does work fine, but my concerns about code smells remain. Id be happy if someone would check, but to not waste anyones time thinking I truly have no solution I will mark this as one.
Code:

const std = @import("std");

pub fn main() !void {
    const addr = try std.net.Address.parseIp("127.0.0.1", 32100);

    const sock = try std.posix.socket(
        std.posix.AF.INET,
        std.posix.SOCK.DGRAM | std.posix.SOCK.NONBLOCK,
        std.posix.IPPROTO.UDP,
    );
    defer std.posix.close(sock);
    try std.posix.bind(sock, &addr.any, addr.getOsSockLen());
    std.log.info("Listen on {any}...", .{addr});

    while (true) {
        var buf: [1024]u8 = undefined;
        var other_addr: std.posix.sockaddr = undefined;
        var other_addrlen: std.posix.socklen_t = @sizeOf(std.posix.sockaddr);

        const len = std.posix.recvfrom(sock, &buf, 0, &other_addr, &other_addrlen) catch |e| switch (e) {
            error.WouldBlock => {
                std.time.sleep(200 * std.time.ns_per_ms);
                continue;
            },
            else => return e,
        };
        const in = buf[0..len];
        std.log.info("{d} sent {s}", .{ other_addr.data[2..6], in });

        // we could extract the source address of the received data by
        // parsing the other_addr.data field
        const n_sent = try std.posix.sendto(sock, in, 0, &other_addr, other_addrlen);
        std.log.info("echoed {d} byte(s) back", .{n_sent});

        // break;
    }
}

Your solution works, but you have to consider this: If all you’re going to do is sleep when the recvfrom would block, then why use non-blocking mode in the first place? If you switch to blocking mode, the code is simplified because the call to recvfrom will block the thread, waiting for a connection, which is more efficient (less CPU usage) that just sleeping and continuing in a tight loop. Non-blocking mode is useful when you can do other things besides sleeping if the recvfrom would block.

1 Like

This was just me testing out the basic concept of nonblocking udp, I am aware that using blocking here would be better if I wasnt going to try and build on this.