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});
}
}
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.
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.