Reading/Writing from/to Unix Domain sockets in 0.15.2

I am trying to read/write from/to Unix Domain sockets and I am a little bit confused. I think the confusion stems from the changes std.io is currently undergoing.
I am basically trying to read from a `std.net.Stream` and am using the `stream.read(buffer)`.
But there is a comment that `read` is deprecated and I should use `Reader` instead, but there is no further information by means of doc comments.

So I am currently doing this:

var header_buffer = [_]u8{0} ** 16;
const n_header = try stream.read(header_buffer[0..]);

and

try stream.writeAll(payload);

How do I have to migrate to the new API?

You should use the stream.reader() function to obtain a Stream.Reader and use its interface, which is a std.Io.Reader.
But I think that the best thing to do is to jump directly into 0.16.0, it’s not that different for readers and writers stuff :slight_smile:

Can you maybe show how to obtain the interface and use it. I tried to do so but didn’t manage because I didn’t manage to obtain the interface from std.net.Reader.

Sure!
This is code that I use inside one of my projects, it uses Zig 0.16.0.

const addr: std.Io.net.IpAddress = std.Io.net.IpAddress.parseLiteral(args.address.?) catch |err| {
                if (err == std.Io.net.IpAddress.ParseLiteralError.InvalidAddress) std.process.fatal("Invalid TCP address.\n", .{});
                if (err == std.Io.net.IpAddress.ParseLiteralError.InvalidPort) std.process.fatal("Invalid TCP port.\n", .{});
                std.process.fatal("Malformed TCP port and address {s}.\n", .{args.address.?});
            };

const stream: std.Io.net.Stream = addr.connect(io, .{ .protocol = .tcp, .mode = .stream }) catch std.process.fatal("Unable to connect to: {s}.\n", .{args.address.?});
defer stream.close(io);

var stream_reader_buf: [4096]u8 = undefined;
var stream_writer_buf: [4096]u8 = undefined;
var stream_reader: std.Io.net.Stream.Reader = stream.reader(io, &stream_reader_buf);
var stream_writer: std.Io.net.Stream.Writer = stream.writer(io, &stream_writer_buf);

The code tries to parse an IP address and connect to it.
The part that interests you is the “stream reader/writer” thing.
A Reader/Writer uses a buffer to optimize syscalls.
The stream.reader and stream.writer functions return a std.Io.Reader and a std.Io.Writer.
Those are the actual interfaces to use for reading and writing.

Here’s the full code if you need more context:
https://codeberg.org/intales/giacomo/src/branch/main/modbuz/src/main.zig

I hope this is useful :smiley:

1 Like

Thank you I will try to update my zig Versio and give it a try… Hope not to much breaks from 0.15.2

2 Likes

despite the title looks you are talking about 0.16

https://codeberg.org/ziglang/zig/pulls/30837

2 Likes

No I was working with 0.15.2 but just now switching to 0.16 as @filo suggested.

I still have to make it work, I was struggling with finding all the right functions and types to work with unix sockets.

But your input might be a good starting point.

1 Like

I now managed to upgrade to 0.16 and at least make it compile. But now I am hitting the point, where I get SegFaults, when trying to write and read to and from the stream via the Reader and Writer interfaces and I think I am somehow not using them correctly.
On the service side, where I accept a connection and use the stream to:

var r_buffer = [_]u8{0} ** 1028;
var w_buffer = [_]u8{0} ** 1028;
const stream_reader = stream.reader(io, r_buffer[0..]);
const stream_writer = stream.writer(io, w_buffer[0..]);
var reader_if = stream_reader.interface;
var writer_if = stream_writer.interface;
var reader = &reader_if;
var writer = &writer_if;

var header_bytes = [_]u8{0} ** 16;

std.debug.print("Reading header\n", .{});
try reader.readSliceAll(header_bytes[0..]);

Here I get following error when I write to the stream on the client side:
Reading header
Segmentation fault at address 0xaaaaad28
/snap/zig/16117/lib/std/Io/net.zig:1305:40: 0x11e5ff4 in readVec (std.zig)
const n = io.vtable.netRead(io.userdata, r.stream.socket.handle, dest) catch |err| {
^
/snap/zig/16117/lib/std/Io/Reader.zig:429:37: 0x10efe1a in readVec (std.zig)
return n + (r.vtable.readVec(r, data[i..]) catch |err| switch (err) {
^
/snap/zig/16117/lib/std/Io/Reader.zig:688:21: 0x10ef3c7 in readSliceShort (std.zig)
i += readVec(r, &data) catch |err| switch (err) {
^
/snap/zig/16117/lib/std/Io/Reader.zig:661:33: 0x10eed37 in readSliceAll (std.zig)
const n = try readSliceShort(r, buffer);
^
/home/giaco/projects/zsomeip_serializer/src/stub.zig:34:36: 0x11d9be1 in wrapper (service.zig)
try reader.readSliceAll(header_bytes[0..]);
^
/home/giaco/projects/zsomeip_serializer/src/service.zig:86:27: 0x11db5df in main (service.zig)
try requestHandler(io, &conn_stream, alloc);
^
/snap/zig/16117/lib/std/start.zig:698:59: 0x11dbd5c in callMain (std.zig)
if (fn_info.params.len == 0) return wrapMain(root.main());
^
/snap/zig/16117/lib/std/start.zig:190:5: 0x11d8201 in _start (std.zig)
asm volatile (switch (native_arch) {
^
Aborted (core dumped)

On the client side, the writing at least seems to work:

std.debug.print("Searialized input.\n", .{});
var w_buffer = [_]u8{0} ** 1024;

const stream_writer = self.stream.writer(self.io, &w_buffer);
var writer_if = stream_writer.interface;
var writer = &writer_if;
try writer.writeAll(input_bytes[0 .. length + 16]);
std.debug.print("Sent input: {any}.\n", .{input_bytes[0 .. length + 16]});

I know this code is probably way from optimal, but I just want to have something working before I start optimizing and rounding off the edges. And more importantly I want to understand how writing and reading from unix sockets is done correctly in Zig.

You fell into the classic beginner Reader/Writer interface trap. :slight_smile:

Here, you’re copying the interface out of the std.Io.net.Stream.Reader/Writer structs, breaking the @fieldParentPtr logic the implementation uses to get at its state. What you need to do instead is this:

var r_buffer = [_]u8{0} ** 1028;
var w_buffer = [_]u8{0} ** 1028;
const stream_reader = stream.reader(io, r_buffer[0..]);
const stream_writer = stream.writer(io, w_buffer[0..]);
var reader = &stream_reader.interface;
var writer = &stream_writer.interface;

var header_bytes = [_]u8{0} ** 16;

std.debug.print("Reading header\n", .{});
try reader.readSliceAll(header_bytes[0..]);

Basically, you MUST skip the copy into reader_if and writer_if and take the address directly from the stream_reader and stream_writer.

2 Likes

Thanks, that was indeed the issue… At least it doesn’t crash anymore ^^
Now it hangs at reading… I hope I can figure this out myself…

Edit: Just had to flush then it didn’t hang any more.

2 Likes

You can also do stream_reader.interface.readSliceAll directly if you don’t need to pass the
interface outside of the current function.
The important thing is to not copy the interface but having a reference to it :slight_smile:

1 Like