UDP send not working

Hello,
I’m trying to make a little game so naturally I decided to use udp for networking. The issue is, that my code just plain does not work. A previous example of my server is able to communicate to nc -u “::1“ 1234(it turns out that it can no longer send messages back but it receives them fine, I dont know why), but the example below which is a minimal version of my games relevant code does not work. I just blocks. Changing to force_nonblocking = true does not change that.

I am not experienced with udp by any means and part of this code is currently a mixture of zigs stdlib and translated c code as I am just trying to do a basic echo within at the moment.

const std = @import("std");

pub const addr_len: u32 = @sizeOf(std.posix.sockaddr);

pub const Server = struct {
    address: std.net.Address,
    socket: std.posix.socket_t,

    const Options = struct {
        client_only: bool = false,
        force_nonblocking: bool = false,
        reuse_address: bool = false,
    };

    pub fn fromAddress(address: std.net.Address, options: Options) !Server {
        const nonblock: u32 = if (options.force_nonblocking) std.posix.SOCK.NONBLOCK else 0;
        const flags = std.posix.SOCK.DGRAM | nonblock;

        const socket = try std.posix.socket(address.any.family, flags, 0);

        if (options.reuse_address) {
            try std.posix.setsockopt(
                socket,
                std.posix.SOL.SOCKET,
                std.posix.SO.REUSEADDR,
                &std.mem.toBytes(@as(c_int, 1)),
            );
        }

        if (!options.client_only) {
            try std.posix.bind(socket, &address.any, address.getOsSockLen());
        }

        return .{
            .address = address,
            .socket = socket,
        };
    }

    pub fn deinit(self: *Server) void {
        std.posix.close(self.socket);
        self.* = undefined;
    }

    pub fn receive(self: *Server, buf: []u8) !struct { std.net.Address, []const u8 } {
        var source: std.net.Address = self.address;
        const read_len = try std.posix
            .recvfrom(self.socket, buf, 0, &source.any, @constCast(&addr_len));
        return .{ source, buf[0..read_len] };
    }

    pub fn send(self: *const Server, destination: std.net.Address, message: []const u8) !void {
        _ = try std.posix.sendto(self.socket, message, 0, &destination.any, addr_len);
    }
};

pub fn main() !void {
    const server_addr: std.net.Address = try .resolveIp("::1", 1234);
    var server_thread: std.Thread = try .spawn(.{}, serverThread, .{server_addr});
    defer server_thread.join();
    std.Thread.sleep(std.time.ns_per_s);

    const client_addr: std.net.Address = try .resolveIp("::1", 0);
    var client: Server = try .fromAddress(client_addr, .{});
    defer client.deinit();

    try client.send(server_addr, "feef");
}

fn serverThread(addr: std.net.Address) !void {
    var server: Server = try .fromAddress(addr, .{ .force_nonblocking = false });
    defer server.deinit();

    var buf: [0x1000]u8 = undefined;
    while (true) {
        const source, const bytes = server.receive(&buf) catch |e| switch (e) {
            error.WouldBlock => {
                std.Thread.sleep(std.time.ns_per_s); // smite me
                continue;
            },
            else => return e,
        };

        try server.send(source, bytes);

        std.debug.print("{f} {s}", .{ source, bytes });
    }
}

I saw that you set addr_len as a global constant.

Then, you applied @constCast to its address to pass it as an argument. This is definitely wrong.

A function requires a mutable reference as an argument, indicating that the function will modify it and output a modified addr_len.

@constCast should only be used when you are certain about what you are doing; under normal circumstances, using it is basically wrong.

For another addr_len usage, I think you should use destination.getOsSockLen(), just like the API you call when using std.posix.bind.

1 Like

Yeah the constcast is something I for somereason just stopped turned blind to. It didn’t make a difference in fixing my program though. I will look more into documentation to see how to properly use recvfrom. getOsSockLen()did, however, do the trick. Thank you!