今天在学习使用 Zig 编写 Tcp 程序,遇到了一个只在 Windows 平台出现、Linux 平台完全正常的标准库 Bug。
代码如下:
zig
const std = @import("std");
pub fn main(init: std.process.Init) !void {
const addr = try std.Io.net.IpAddress.parse("127.0.0.1", 3000);
var server = try addr.listen(init.io, .{});
defer server.deinit(init.io);
std.debug.print("[Info] listen 127.0.0.1:3000\n", .{});
while (server.accept(init.io)) |stream| {
std.debug.print("[Info] accept {}\n", .{stream.socket.address});
_ = std.Thread.spawn(.{}, handleConnect, .{ init.io, stream }) catch |err| {
stream.close(init.io);
std.debug.print("[Error] Thread.spawn error: {}\n", .{err});
};
} else |err| {
return err;
}
}
fn handleConnect(io: std.Io, stream: std.Io.net.Stream) !void {
defer stream.close(io);
var buf: [4096]u8 = undefined;
const msg = try stream.socket.receive(io, &buf);
std.debug.print("[Info] client message: {s}\n", .{msg.data});
}
问题出在这一行:
zig
const msg = try stream.socket.receive(io, &buf);
在 Windows 平台调用时,会直接返回错误:
error.Unexpected NTSTATUS=0xc000000d (INVALID_PARAMETER)
一开始我以为是我调用方式有问题,于是换了另一种写法:
zig
fn handleConnect(io: std.Io, stream: std.Io.net.Stream) !void {
defer stream.close(io);
// var buf: [4096]u8 = undefined;
// const msg = try stream.socket.receive(io, &buf);
// std.debug.print("[Info] client message: {s}\n", .{msg.data});
var reader_buf: [4096]u8 = undefined;
var reader = stream.reader(io, &reader_buf);
var buf: [4096]u8 = undefined;
var writer = std.Io.Writer.fixed(&buf);
try reader.interface.streamExact(&writer, 10);
std.debug.print("[Info] client message: {s}\n", .{writer.buffered()});
}
使用上面的调用方式,就可以正常的获取客户端发送的消息了。但是这个调用方式只是用 Reader 包装了一下,其他基本逻辑是没有变化的。
所以我开始怀疑是不是 stream.socket.receive 这个函数的实现有 Bug。毕竟这个函数的使用方式是那么简单,提供一个缓冲区,用于接收客户端发送过来的数据,几乎不存在误用的可能。而新的调用方式也证明了我的其他代码逻辑是没有问题的,可以正常接收客户端数据。
我将相同的代码交叉编译为 Linux 版本,并部署到 Linux 环境中进行测试。结果非常完美,相同的代码,在 Linux 上可以正常工作,但是到 Windows 就会报错,我可以确定,这就是 Zig 0.16 标准库在 Windows 的实现 Bug。