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.
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
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.
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:
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.
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.
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