Commit 77f8a9ae22
Changed files (5)
lib
std
lib/std/os/windows/ws2_32.zig
@@ -266,10 +266,54 @@ pub const SENDER_DEFAULT_LATE_JOINER_PERCENTAGE = 0;
pub const SENDER_MAX_LATE_JOINER_PERCENTAGE = 75;
pub const BITS_PER_BYTE = 8;
pub const LOG2_BITS_PER_BYTE = 3;
+
pub const SOCKET_DEFAULT2_QM_POLICY = GUID.parse("{aec2ef9c-3a4d-4d3e-8842-239942e39a47}");
pub const REAL_TIME_NOTIFICATION_CAPABILITY = GUID.parse("{6b59819a-5cae-492d-a901-2a3c2c50164f}");
pub const REAL_TIME_NOTIFICATION_CAPABILITY_EX = GUID.parse("{6843da03-154a-4616-a508-44371295f96b}");
pub const ASSOCIATE_NAMERES_CONTEXT = GUID.parse("{59a38b67-d4fe-46e1-ba3c-87ea74ca3049}");
+
+pub const WSAID_CONNECTEX = GUID{
+ .Data1 = 0x25a207b9,
+ .Data2 = 0xddf3,
+ .Data3 = 0x4660,
+ .Data4 = [8]u8{ 0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e },
+};
+
+pub const WSAID_ACCEPTEX = GUID{
+ .Data1 = 0xb5367df1,
+ .Data2 = 0xcbac,
+ .Data3 = 0x11cf,
+ .Data4 = [8]u8{ 0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92 },
+};
+
+pub const WSAID_GETACCEPTEXSOCKADDRS = GUID{
+ .Data1 = 0xb5367df2,
+ .Data2 = 0xcbac,
+ .Data3 = 0x11cf,
+ .Data4 = [8]u8{ 0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92 },
+};
+
+pub const WSAID_WSARECVMSG = GUID{
+ .Data1 = 0xf689d7c8,
+ .Data2 = 0x6f1f,
+ .Data3 = 0x436b,
+ .Data4 = [8]u8{ 0x8a, 0x53, 0xe5, 0x4f, 0xe3, 0x51, 0xc3, 0x22 },
+};
+
+pub const WSAID_WSAPOLL = GUID{
+ .Data1 = 0x18C76F85,
+ .Data2 = 0xDC66,
+ .Data3 = 0x4964,
+ .Data4 = [8]u8{ 0x97, 0x2E, 0x23, 0xC2, 0x72, 0x38, 0x31, 0x2B },
+};
+
+pub const WSAID_WSASENDMSG = GUID{
+ .Data1 = 0xa441e712,
+ .Data2 = 0x754f,
+ .Data3 = 0x43ca,
+ .Data4 = [8]u8{ 0x84, 0xa7, 0x0d, 0xee, 0x44, 0xcf, 0x60, 0x6d },
+};
+
pub const TCP_INITIAL_RTO_DEFAULT_RTT = 0;
pub const TCP_INITIAL_RTO_DEFAULT_MAX_SYN_RETRANSMISSIONS = 0;
pub const SOCKET_SETTINGS_GUARANTEE_ENCRYPTION = 1;
@@ -485,6 +529,7 @@ pub const IOC_UNIX = 0;
pub const IOC_WS2 = 134217728;
pub const IOC_PROTOCOL = 268435456;
pub const IOC_VENDOR = 402653184;
+pub const SIO_GET_EXTENSION_FUNCTION_POINTER = IOC_OUT | IOC_IN | IOC_WS2 | 6;
pub const SIO_BSP_HANDLE = IOC_OUT | IOC_WS2 | 27;
pub const SIO_BSP_HANDLE_SELECT = IOC_OUT | IOC_WS2 | 28;
pub const SIO_BSP_HANDLE_POLL = IOC_OUT | IOC_WS2 | 29;
@@ -1115,9 +1160,9 @@ pub const LPFN_GETACCEPTEXSOCKADDRS = fn (
RemoteSockaddrLength: *i32,
) callconv(WINAPI) void;
-pub const LFN_WSASENDMSG = fn (
+pub const LPFN_WSASENDMSG = fn (
s: SOCKET,
- lpMsg: *WSAMSG_const,
+ lpMsg: *const WSAMSG_const,
dwFlags: u32,
lpNumberOfBytesSent: ?*u32,
lpOverlapped: ?*OVERLAPPED,
@@ -1927,7 +1972,7 @@ pub extern "ws2_32" fn WSAHtons(
pub extern "ws2_32" fn WSAIoctl(
s: SOCKET,
dwIoControlCode: u32,
- lpvInBuffer: ?*c_void,
+ lpvInBuffer: ?*const c_void,
cbInBuffer: u32,
lpvOutbuffer: ?*c_void,
cbOutbuffer: u32,
@@ -1992,7 +2037,7 @@ pub extern "ws2_32" fn WSASend(
s: SOCKET,
lpBuffers: [*]WSABUF,
dwBufferCount: u32,
- lpNumberOfBytesSent: ?*U32,
+ lpNumberOfBytesSent: ?*u32,
dwFlags: u32,
lpOverlapped: ?*OVERLAPPED,
lpCompletionRoutine: ?LPWSAOVERLAPPED_COMPLETION_ROUTINE,
@@ -2000,7 +2045,7 @@ pub extern "ws2_32" fn WSASend(
pub extern "ws2_32" fn WSASendMsg(
s: SOCKET,
- lpMsg: *WSAMSG_const,
+ lpMsg: *const WSAMSG_const,
dwFlags: u32,
lpNumberOfBytesSent: ?*u32,
lpOverlapped: ?*OVERLAPPED,
lib/std/os/windows.zig
@@ -1749,6 +1749,38 @@ fn MAKELANGID(p: c_ushort, s: c_ushort) callconv(.Inline) LANGID {
return (s << 10) | p;
}
+/// Loads a Winsock extension function in runtime specified by a GUID.
+pub fn loadWinsockExtensionFunction(comptime T: type, sock: ws2_32.SOCKET, guid: GUID) !T {
+ var function: T = undefined;
+ var num_bytes: DWORD = undefined;
+
+ const rc = ws2_32.WSAIoctl(
+ sock,
+ ws2_32.SIO_GET_EXTENSION_FUNCTION_POINTER,
+ @ptrCast(*const c_void, &guid),
+ @sizeOf(GUID),
+ &function,
+ @sizeOf(T),
+ &num_bytes,
+ null,
+ null,
+ );
+
+ if (rc == ws2_32.SOCKET_ERROR) {
+ return switch (ws2_32.WSAGetLastError()) {
+ .WSAEOPNOTSUPP => error.OperationNotSupported,
+ .WSAENOTSOCK => error.FileDescriptorNotASocket,
+ else => |err| unexpectedWSAError(err),
+ };
+ }
+
+ if (num_bytes != @sizeOf(T)) {
+ return error.ShortRead;
+ }
+
+ return function;
+}
+
/// Call this when you made a windows DLL call or something that does SetLastError
/// and you get an unexpected error.
pub fn unexpectedError(err: Win32Error) std.os.UnexpectedError {
lib/std/x/os/socket.zig
@@ -13,105 +13,111 @@ const mem = std.mem;
const time = std.time;
const builtin = std.builtin;
-/// Import in a `Socket` abstraction depending on the platform we are compiling against.
-pub usingnamespace switch (builtin.os.tag) {
- .windows => @import("socket_windows.zig"),
- else => @import("socket_posix.zig"),
-};
+/// A generic, cross-platform socket abstraction.
+pub const Socket = struct {
+ /// A socket-address pair.
+ pub const Connection = struct {
+ socket: Socket,
+ address: Socket.Address,
-/// A common subset of shared structs across cross-platform abstractions over socket syscalls.
-pub fn Mixin(comptime Self: type) type {
- return struct {
- /// A socket-address pair.
- pub const Connection = struct {
- socket: Self,
- address: Self.Address,
+ /// Enclose a socket and address into a socket-address pair.
+ pub fn from(socket: Socket, address: Socket.Address) Socket.Connection {
+ return .{ .socket = socket, .address = address };
+ }
+ };
- /// Enclose a socket and address into a socket-address pair.
- pub fn from(socket: Self, address: Self.Address) Self.Connection {
- return .{ .socket = socket, .address = address };
- }
- };
+ /// A generic socket address abstraction. It is safe to directly access and modify
+ /// the fields of a `Socket.Address`.
+ pub const Address = union(enum) {
+ ipv4: net.IPv4.Address,
+ ipv6: net.IPv6.Address,
- /// A generic socket address abstraction. It is safe to directly access and modify
- /// the fields of a `Self.Address`.
- pub const Address = union(enum) {
- ipv4: net.IPv4.Address,
- ipv6: net.IPv6.Address,
+ /// Instantiate a new address with a IPv4 host and port.
+ pub fn initIPv4(host: net.IPv4, port: u16) Socket.Address {
+ return .{ .ipv4 = .{ .host = host, .port = port } };
+ }
- /// Instantiate a new address with a IPv4 host and port.
- pub fn initIPv4(host: net.IPv4, port: u16) Self.Address {
- return .{ .ipv4 = .{ .host = host, .port = port } };
- }
+ /// Instantiate a new address with a IPv6 host and port.
+ pub fn initIPv6(host: net.IPv6, port: u16) Socket.Address {
+ return .{ .ipv6 = .{ .host = host, .port = port } };
+ }
- /// Instantiate a new address with a IPv6 host and port.
- pub fn initIPv6(host: net.IPv6, port: u16) Self.Address {
- return .{ .ipv6 = .{ .host = host, .port = port } };
- }
-
- /// Parses a `sockaddr` into a generic socket address.
- pub fn fromNative(address: *align(4) const os.sockaddr) Self.Address {
- switch (address.family) {
- os.AF_INET => {
- const info = @ptrCast(*const os.sockaddr_in, address);
- const host = net.IPv4{ .octets = @bitCast([4]u8, info.addr) };
- const port = mem.bigToNative(u16, info.port);
- return Self.Address.initIPv4(host, port);
- },
- os.AF_INET6 => {
- const info = @ptrCast(*const os.sockaddr_in6, address);
- const host = net.IPv6{ .octets = info.addr, .scope_id = info.scope_id };
- const port = mem.bigToNative(u16, info.port);
- return Self.Address.initIPv6(host, port);
- },
- else => unreachable,
- }
+ /// Parses a `sockaddr` into a generic socket address.
+ pub fn fromNative(address: *align(4) const os.sockaddr) Socket.Address {
+ switch (address.family) {
+ os.AF_INET => {
+ const info = @ptrCast(*const os.sockaddr_in, address);
+ const host = net.IPv4{ .octets = @bitCast([4]u8, info.addr) };
+ const port = mem.bigToNative(u16, info.port);
+ return Socket.Address.initIPv4(host, port);
+ },
+ os.AF_INET6 => {
+ const info = @ptrCast(*const os.sockaddr_in6, address);
+ const host = net.IPv6{ .octets = info.addr, .scope_id = info.scope_id };
+ const port = mem.bigToNative(u16, info.port);
+ return Socket.Address.initIPv6(host, port);
+ },
+ else => unreachable,
}
+ }
- /// Encodes a generic socket address into an extern union that may be reliably
- /// casted into a `sockaddr` which may be passed into socket syscalls.
- pub fn toNative(self: Self.Address) extern union {
- ipv4: os.sockaddr_in,
- ipv6: os.sockaddr_in6,
- } {
- return switch (self) {
- .ipv4 => |address| .{
- .ipv4 = .{
- .addr = @bitCast(u32, address.host.octets),
- .port = mem.nativeToBig(u16, address.port),
- },
+ /// Encodes a generic socket address into an extern union that may be reliably
+ /// casted into a `sockaddr` which may be passed into socket syscalls.
+ pub fn toNative(self: Socket.Address) extern union {
+ ipv4: os.sockaddr_in,
+ ipv6: os.sockaddr_in6,
+ } {
+ return switch (self) {
+ .ipv4 => |address| .{
+ .ipv4 = .{
+ .addr = @bitCast(u32, address.host.octets),
+ .port = mem.nativeToBig(u16, address.port),
},
- .ipv6 => |address| .{
- .ipv6 = .{
- .addr = address.host.octets,
- .port = mem.nativeToBig(u16, address.port),
- .scope_id = address.host.scope_id,
- .flowinfo = 0,
- },
+ },
+ .ipv6 => |address| .{
+ .ipv6 = .{
+ .addr = address.host.octets,
+ .port = mem.nativeToBig(u16, address.port),
+ .scope_id = address.host.scope_id,
+ .flowinfo = 0,
},
- };
- }
+ },
+ };
+ }
- /// Returns the number of bytes that make up the `sockaddr` equivalent to the address.
- pub fn getNativeSize(self: Self.Address) u32 {
- return switch (self) {
- .ipv4 => @sizeOf(os.sockaddr_in),
- .ipv6 => @sizeOf(os.sockaddr_in6),
- };
- }
+ /// Returns the number of bytes that make up the `sockaddr` equivalent to the address.
+ pub fn getNativeSize(self: Socket.Address) u32 {
+ return switch (self) {
+ .ipv4 => @sizeOf(os.sockaddr_in),
+ .ipv6 => @sizeOf(os.sockaddr_in6),
+ };
+ }
- /// Implements the `std.fmt.format` API.
- pub fn format(
- self: Self.Address,
- comptime layout: []const u8,
- opts: fmt.FormatOptions,
- writer: anytype,
- ) !void {
- switch (self) {
- .ipv4 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }),
- .ipv6 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }),
- }
+ /// Implements the `std.fmt.format` API.
+ pub fn format(
+ self: Socket.Address,
+ comptime layout: []const u8,
+ opts: fmt.FormatOptions,
+ writer: anytype,
+ ) !void {
+ switch (self) {
+ .ipv4 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }),
+ .ipv6 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }),
}
- };
+ }
};
-}
+
+ /// The underlying handle of a socket.
+ fd: os.socket_t,
+
+ /// Enclose a socket abstraction over an existing socket file descriptor.
+ pub fn from(fd: os.socket_t) Socket {
+ return Socket{ .fd = fd };
+ }
+
+ /// Mix in socket syscalls depending on the platform we are compiling against.
+ pub usingnamespace switch (builtin.os.tag) {
+ .windows => @import("socket_windows.zig"),
+ else => @import("socket_posix.zig"),
+ }.Mixin(Socket);
+};
lib/std/x/os/socket_posix.zig
@@ -10,232 +10,242 @@ const os = std.os;
const mem = std.mem;
const time = std.time;
-pub const Socket = struct {
- /// Import in `Socket.Address` and `Socket.Connection`.
- pub usingnamespace @import("socket.zig").Mixin(Socket);
-
- /// The underlying handle of a socket.
- fd: os.socket_t,
-
- /// Open a new socket.
- pub fn init(domain: u32, socket_type: u32, protocol: u32) !Socket {
- return Socket{ .fd = try os.socket(domain, socket_type, protocol) };
- }
-
- /// Enclose a socket abstraction over an existing socket file descriptor.
- pub fn from(fd: os.socket_t) Socket {
- return Socket{ .fd = fd };
- }
-
- /// Closes the socket.
- pub fn deinit(self: Socket) void {
- os.closeSocket(self.fd);
- }
-
- /// Shutdown either the read side, write side, or all side of the socket.
- pub fn shutdown(self: Socket, how: os.ShutdownHow) !void {
- return os.shutdown(self.fd, how);
- }
-
- /// Binds the socket to an address.
- pub fn bind(self: Socket, address: Socket.Address) !void {
- return os.bind(self.fd, @ptrCast(*const os.sockaddr, &address.toNative()), address.getNativeSize());
- }
-
- /// Start listening for incoming connections on the socket.
- pub fn listen(self: Socket, max_backlog_size: u31) !void {
- return os.listen(self.fd, max_backlog_size);
- }
-
- /// Have the socket attempt to the connect to an address.
- pub fn connect(self: Socket, address: Socket.Address) !void {
- return os.connect(self.fd, @ptrCast(*const os.sockaddr, &address.toNative()), address.getNativeSize());
- }
-
- /// Accept a pending incoming connection queued to the kernel backlog
- /// of the socket.
- pub fn accept(self: Socket, flags: u32) !Socket.Connection {
- var address: os.sockaddr_storage = undefined;
- var address_len: u32 = @sizeOf(os.sockaddr_storage);
-
- const socket = Socket{ .fd = try os.accept(self.fd, @ptrCast(*os.sockaddr, &address), &address_len, flags) };
- const socket_address = Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address));
-
- return Socket.Connection.from(socket, socket_address);
- }
-
- /// Read data from the socket into the buffer provided with a set of flags
- /// specified. It returns the number of bytes read into the buffer provided.
- pub fn read(self: Socket, buf: []u8, flags: u32) !usize {
- return os.recv(self.fd, buf, flags);
- }
-
- /// Write a buffer of data provided to the socket with a set of flags specified.
- /// It returns the number of bytes that are written to the socket.
- pub fn write(self: Socket, buf: []const u8, flags: u32) !usize {
- return os.send(self.fd, buf, flags);
- }
-
- /// Writes multiple I/O vectors with a prepended message header to the socket
- /// with a set of flags specified. It returns the number of bytes that are
- /// written to the socket.
- pub fn writeVectorized(self: Socket, msg: os.msghdr_const, flags: u32) !usize {
- return os.sendmsg(self.fd, msg, flags);
- }
-
- /// Read multiple I/O vectors with a prepended message header from the socket
- /// with a set of flags specified. It returns the number of bytes that were
- /// read into the buffer provided.
- pub fn readVectorized(self: Socket, msg: *os.msghdr, flags: u32) !usize {
- return error.NotImplemented;
- }
-
- /// Query the address that the socket is locally bounded to.
- pub fn getLocalAddress(self: Socket) !Socket.Address {
- var address: os.sockaddr_storage = undefined;
- var address_len: u32 = @sizeOf(os.sockaddr_storage);
- try os.getsockname(self.fd, @ptrCast(*os.sockaddr, &address), &address_len);
- return Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address));
- }
-
- /// Query the address that the socket is connected to.
- pub fn getRemoteAddress(self: Socket) !Socket.Address {
- var address: os.sockaddr_storage = undefined;
- var address_len: u32 = @sizeOf(os.sockaddr_storage);
- try os.getpeername(self.fd, @ptrCast(*os.sockaddr, &address), &address_len);
- return Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address));
- }
-
- /// Query and return the latest cached error on the socket.
- pub fn getError(self: Socket) !void {
- return os.getsockoptError(self.fd);
- }
-
- /// Query the read buffer size of the socket.
- pub fn getReadBufferSize(self: Socket) !u32 {
- var value: u32 = undefined;
- var value_len: u32 = @sizeOf(u32);
-
- const rc = os.system.getsockopt(self.fd, os.SOL_SOCKET, os.SO_RCVBUF, mem.asBytes(&value), &value_len);
- return switch (os.errno(rc)) {
- 0 => value,
- os.EBADF => error.BadFileDescriptor,
- os.EFAULT => error.InvalidAddressSpace,
- os.EINVAL => error.InvalidSocketOption,
- os.ENOPROTOOPT => error.UnknownSocketOption,
- os.ENOTSOCK => error.NotASocket,
- else => |err| os.unexpectedErrno(err),
- };
- }
-
- /// Query the write buffer size of the socket.
- pub fn getWriteBufferSize(self: Socket) !u32 {
- var value: u32 = undefined;
- var value_len: u32 = @sizeOf(u32);
-
- const rc = os.system.getsockopt(self.fd, os.SOL_SOCKET, os.SO_SNDBUF, mem.asBytes(&value), &value_len);
- return switch (os.errno(rc)) {
- 0 => value,
- os.EBADF => error.BadFileDescriptor,
- os.EFAULT => error.InvalidAddressSpace,
- os.EINVAL => error.InvalidSocketOption,
- os.ENOPROTOOPT => error.UnknownSocketOption,
- os.ENOTSOCK => error.NotASocket,
- else => |err| os.unexpectedErrno(err),
- };
- }
-
- /// Set a socket option.
- pub fn setOption(self: Socket, level: u32, code: u32, value: []const u8) !void {
- return os.setsockopt(self.fd, level, code, value);
- }
-
- /// Have close() or shutdown() syscalls block until all queued messages in the socket have been successfully
- /// sent, or if the timeout specified in seconds has been reached. It returns `error.UnsupportedSocketOption`
- /// if the host does not support the option for a socket to linger around up until a timeout specified in
- /// seconds.
- pub fn setLinger(self: Socket, timeout_seconds: ?u16) !void {
- if (comptime @hasDecl(os, "SO_LINGER")) {
- const settings = extern struct {
- l_onoff: c_int,
- l_linger: c_int,
- }{
- .l_onoff = @intCast(c_int, @boolToInt(timeout_seconds != null)),
- .l_linger = if (timeout_seconds) |seconds| @intCast(c_int, seconds) else 0,
+pub fn Mixin(comptime Socket: type) type {
+ return struct {
+ /// Open a new socket.
+ pub fn init(domain: u32, socket_type: u32, protocol: u32) !Socket {
+ return Socket{ .fd = try os.socket(domain, socket_type, protocol) };
+ }
+
+ /// Closes the socket.
+ pub fn deinit(self: Socket) void {
+ os.closeSocket(self.fd);
+ }
+
+ /// Shutdown either the read side, write side, or all side of the socket.
+ pub fn shutdown(self: Socket, how: os.ShutdownHow) !void {
+ return os.shutdown(self.fd, how);
+ }
+
+ /// Binds the socket to an address.
+ pub fn bind(self: Socket, address: Socket.Address) !void {
+ return os.bind(self.fd, @ptrCast(*const os.sockaddr, &address.toNative()), address.getNativeSize());
+ }
+
+ /// Start listening for incoming connections on the socket.
+ pub fn listen(self: Socket, max_backlog_size: u31) !void {
+ return os.listen(self.fd, max_backlog_size);
+ }
+
+ /// Have the socket attempt to the connect to an address.
+ pub fn connect(self: Socket, address: Socket.Address) !void {
+ return os.connect(self.fd, @ptrCast(*const os.sockaddr, &address.toNative()), address.getNativeSize());
+ }
+
+ /// Accept a pending incoming connection queued to the kernel backlog
+ /// of the socket.
+ pub fn accept(self: Socket, flags: u32) !Socket.Connection {
+ var address: os.sockaddr_storage = undefined;
+ var address_len: u32 = @sizeOf(os.sockaddr_storage);
+
+ const socket = Socket{ .fd = try os.accept(self.fd, @ptrCast(*os.sockaddr, &address), &address_len, flags) };
+ const socket_address = Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address));
+
+ return Socket.Connection.from(socket, socket_address);
+ }
+
+ /// Read data from the socket into the buffer provided with a set of flags
+ /// specified. It returns the number of bytes read into the buffer provided.
+ pub fn read(self: Socket, buf: []u8, flags: u32) !usize {
+ return os.recv(self.fd, buf, flags);
+ }
+
+ /// Write a buffer of data provided to the socket with a set of flags specified.
+ /// It returns the number of bytes that are written to the socket.
+ pub fn write(self: Socket, buf: []const u8, flags: u32) !usize {
+ return os.send(self.fd, buf, flags);
+ }
+
+ /// Writes multiple I/O vectors with a prepended message header to the socket
+ /// with a set of flags specified. It returns the number of bytes that are
+ /// written to the socket.
+ pub fn writeVectorized(self: Socket, msg: os.msghdr_const, flags: u32) !usize {
+ return os.sendmsg(self.fd, msg, flags);
+ }
+
+ /// Read multiple I/O vectors with a prepended message header from the socket
+ /// with a set of flags specified. It returns the number of bytes that were
+ /// read into the buffer provided.
+ pub fn readVectorized(self: Socket, msg: *os.msghdr, flags: u32) !usize {
+ if (comptime @hasDecl(os.system, "recvmsg")) {
+ while (true) {
+ const rc = os.system.recvmsg(self.fd, msg, flags);
+ return switch (os.errno(rc)) {
+ 0 => @intCast(usize, rc),
+ os.EBADF => unreachable, // always a race condition
+ os.EFAULT => unreachable,
+ os.EINVAL => unreachable,
+ os.ENOTCONN => unreachable,
+ os.ENOTSOCK => unreachable,
+ os.EINTR => continue,
+ os.EAGAIN => error.WouldBlock,
+ os.ENOMEM => error.SystemResources,
+ os.ECONNREFUSED => error.ConnectionRefused,
+ os.ECONNRESET => error.ConnectionResetByPeer,
+ else => |err| os.unexpectedErrno(err),
+ };
+ }
+ }
+ return error.NotSupported;
+ }
+
+ /// Query the address that the socket is locally bounded to.
+ pub fn getLocalAddress(self: Socket) !Socket.Address {
+ var address: os.sockaddr_storage = undefined;
+ var address_len: u32 = @sizeOf(os.sockaddr_storage);
+ try os.getsockname(self.fd, @ptrCast(*os.sockaddr, &address), &address_len);
+ return Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address));
+ }
+
+ /// Query the address that the socket is connected to.
+ pub fn getRemoteAddress(self: Socket) !Socket.Address {
+ var address: os.sockaddr_storage = undefined;
+ var address_len: u32 = @sizeOf(os.sockaddr_storage);
+ try os.getpeername(self.fd, @ptrCast(*os.sockaddr, &address), &address_len);
+ return Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address));
+ }
+
+ /// Query and return the latest cached error on the socket.
+ pub fn getError(self: Socket) !void {
+ return os.getsockoptError(self.fd);
+ }
+
+ /// Query the read buffer size of the socket.
+ pub fn getReadBufferSize(self: Socket) !u32 {
+ var value: u32 = undefined;
+ var value_len: u32 = @sizeOf(u32);
+
+ const rc = os.system.getsockopt(self.fd, os.SOL_SOCKET, os.SO_RCVBUF, mem.asBytes(&value), &value_len);
+ return switch (os.errno(rc)) {
+ 0 => value,
+ os.EBADF => error.BadFileDescriptor,
+ os.EFAULT => error.InvalidAddressSpace,
+ os.EINVAL => error.InvalidSocketOption,
+ os.ENOPROTOOPT => error.UnknownSocketOption,
+ os.ENOTSOCK => error.NotASocket,
+ else => |err| os.unexpectedErrno(err),
};
+ }
+
+ /// Query the write buffer size of the socket.
+ pub fn getWriteBufferSize(self: Socket) !u32 {
+ var value: u32 = undefined;
+ var value_len: u32 = @sizeOf(u32);
+
+ const rc = os.system.getsockopt(self.fd, os.SOL_SOCKET, os.SO_SNDBUF, mem.asBytes(&value), &value_len);
+ return switch (os.errno(rc)) {
+ 0 => value,
+ os.EBADF => error.BadFileDescriptor,
+ os.EFAULT => error.InvalidAddressSpace,
+ os.EINVAL => error.InvalidSocketOption,
+ os.ENOPROTOOPT => error.UnknownSocketOption,
+ os.ENOTSOCK => error.NotASocket,
+ else => |err| os.unexpectedErrno(err),
+ };
+ }
+
+ /// Set a socket option.
+ pub fn setOption(self: Socket, level: u32, code: u32, value: []const u8) !void {
+ return os.setsockopt(self.fd, level, code, value);
+ }
+
+ /// Have close() or shutdown() syscalls block until all queued messages in the socket have been successfully
+ /// sent, or if the timeout specified in seconds has been reached. It returns `error.UnsupportedSocketOption`
+ /// if the host does not support the option for a socket to linger around up until a timeout specified in
+ /// seconds.
+ pub fn setLinger(self: Socket, timeout_seconds: ?u16) !void {
+ if (comptime @hasDecl(os, "SO_LINGER")) {
+ const settings = extern struct {
+ l_onoff: c_int,
+ l_linger: c_int,
+ }{
+ .l_onoff = @intCast(c_int, @boolToInt(timeout_seconds != null)),
+ .l_linger = if (timeout_seconds) |seconds| @intCast(c_int, seconds) else 0,
+ };
+
+ return self.setOption(os.SOL_SOCKET, os.SO_LINGER, mem.asBytes(&settings));
+ }
+
+ return error.UnsupportedSocketOption;
+ }
+
+ /// On connection-oriented sockets, have keep-alive messages be sent periodically. The timing in which keep-alive
+ /// messages are sent are dependant on operating system settings. It returns `error.UnsupportedSocketOption` if
+ /// the host does not support periodically sending keep-alive messages on connection-oriented sockets.
+ pub fn setKeepAlive(self: Socket, enabled: bool) !void {
+ if (comptime @hasDecl(os, "SO_KEEPALIVE")) {
+ return self.setOption(os.SOL_SOCKET, os.SO_KEEPALIVE, mem.asBytes(&@as(u32, @boolToInt(enabled))));
+ }
+ return error.UnsupportedSocketOption;
+ }
+
+ /// Allow multiple sockets on the same host to listen on the same address. It returns `error.UnsupportedSocketOption` if
+ /// the host does not support sockets listening the same address.
+ pub fn setReuseAddress(self: Socket, enabled: bool) !void {
+ if (comptime @hasDecl(os, "SO_REUSEADDR")) {
+ return self.setOption(os.SOL_SOCKET, os.SO_REUSEADDR, mem.asBytes(&@as(u32, @boolToInt(enabled))));
+ }
+ return error.UnsupportedSocketOption;
+ }
- return self.setOption(os.SOL_SOCKET, os.SO_LINGER, mem.asBytes(&settings));
- }
-
- return error.UnsupportedSocketOption;
- }
-
- /// On connection-oriented sockets, have keep-alive messages be sent periodically. The timing in which keep-alive
- /// messages are sent are dependant on operating system settings. It returns `error.UnsupportedSocketOption` if
- /// the host does not support periodically sending keep-alive messages on connection-oriented sockets.
- pub fn setKeepAlive(self: Socket, enabled: bool) !void {
- if (comptime @hasDecl(os, "SO_KEEPALIVE")) {
- return self.setOption(os.SOL_SOCKET, os.SO_KEEPALIVE, mem.asBytes(&@as(u32, @boolToInt(enabled))));
- }
- return error.UnsupportedSocketOption;
- }
-
- /// Allow multiple sockets on the same host to listen on the same address. It returns `error.UnsupportedSocketOption` if
- /// the host does not support sockets listening the same address.
- pub fn setReuseAddress(self: Socket, enabled: bool) !void {
- if (comptime @hasDecl(os, "SO_REUSEADDR")) {
- return self.setOption(os.SOL_SOCKET, os.SO_REUSEADDR, mem.asBytes(&@as(u32, @boolToInt(enabled))));
- }
- return error.UnsupportedSocketOption;
- }
-
- /// Allow multiple sockets on the same host to listen on the same port. It returns `error.UnsupportedSocketOption` if
- /// the host does not supports sockets listening on the same port.
- pub fn setReusePort(self: Socket, enabled: bool) !void {
- if (comptime @hasDecl(os, "SO_REUSEPORT")) {
- return self.setOption(os.SOL_SOCKET, os.SO_REUSEPORT, mem.asBytes(&@as(u32, @boolToInt(enabled))));
- }
- return error.UnsupportedSocketOption;
- }
-
- /// Set the write buffer size of the socket.
- pub fn setWriteBufferSize(self: Socket, size: u32) !void {
- return self.setOption(os.SOL_SOCKET, os.SO_SNDBUF, mem.asBytes(&size));
- }
-
- /// Set the read buffer size of the socket.
- pub fn setReadBufferSize(self: Socket, size: u32) !void {
- return self.setOption(os.SOL_SOCKET, os.SO_RCVBUF, mem.asBytes(&size));
- }
-
- /// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is
- /// set on a non-blocking socket.
- ///
- /// Set a timeout on the socket that is to occur if no messages are successfully written
- /// to its bound destination after a specified number of milliseconds. A subsequent write
- /// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded.
- pub fn setWriteTimeout(self: Socket, milliseconds: usize) !void {
- const timeout = os.timeval{
- .tv_sec = @intCast(i32, milliseconds / time.ms_per_s),
- .tv_usec = @intCast(i32, (milliseconds % time.ms_per_s) * time.us_per_ms),
- };
-
- return self.setOption(os.SOL_SOCKET, os.SO_SNDTIMEO, mem.asBytes(&timeout));
- }
-
- /// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is
- /// set on a non-blocking socket.
- ///
- /// Set a timeout on the socket that is to occur if no messages are successfully read
- /// from its bound destination after a specified number of milliseconds. A subsequent
- /// read from the socket will thereafter return `error.WouldBlock` should the timeout be
- /// exceeded.
- pub fn setReadTimeout(self: Socket, milliseconds: usize) !void {
- const timeout = os.timeval{
- .tv_sec = @intCast(i32, milliseconds / time.ms_per_s),
- .tv_usec = @intCast(i32, (milliseconds % time.ms_per_s) * time.us_per_ms),
- };
-
- return self.setOption(os.SOL_SOCKET, os.SO_RCVTIMEO, mem.asBytes(&timeout));
- }
-};
+ /// Allow multiple sockets on the same host to listen on the same port. It returns `error.UnsupportedSocketOption` if
+ /// the host does not supports sockets listening on the same port.
+ pub fn setReusePort(self: Socket, enabled: bool) !void {
+ if (comptime @hasDecl(os, "SO_REUSEPORT")) {
+ return self.setOption(os.SOL_SOCKET, os.SO_REUSEPORT, mem.asBytes(&@as(u32, @boolToInt(enabled))));
+ }
+ return error.UnsupportedSocketOption;
+ }
+
+ /// Set the write buffer size of the socket.
+ pub fn setWriteBufferSize(self: Socket, size: u32) !void {
+ return self.setOption(os.SOL_SOCKET, os.SO_SNDBUF, mem.asBytes(&size));
+ }
+
+ /// Set the read buffer size of the socket.
+ pub fn setReadBufferSize(self: Socket, size: u32) !void {
+ return self.setOption(os.SOL_SOCKET, os.SO_RCVBUF, mem.asBytes(&size));
+ }
+
+ /// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is
+ /// set on a non-blocking socket.
+ ///
+ /// Set a timeout on the socket that is to occur if no messages are successfully written
+ /// to its bound destination after a specified number of milliseconds. A subsequent write
+ /// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded.
+ pub fn setWriteTimeout(self: Socket, milliseconds: usize) !void {
+ const timeout = os.timeval{
+ .tv_sec = @intCast(i32, milliseconds / time.ms_per_s),
+ .tv_usec = @intCast(i32, (milliseconds % time.ms_per_s) * time.us_per_ms),
+ };
+
+ return self.setOption(os.SOL_SOCKET, os.SO_SNDTIMEO, mem.asBytes(&timeout));
+ }
+
+ /// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is
+ /// set on a non-blocking socket.
+ ///
+ /// Set a timeout on the socket that is to occur if no messages are successfully read
+ /// from its bound destination after a specified number of milliseconds. A subsequent
+ /// read from the socket will thereafter return `error.WouldBlock` should the timeout be
+ /// exceeded.
+ pub fn setReadTimeout(self: Socket, milliseconds: usize) !void {
+ const timeout = os.timeval{
+ .tv_sec = @intCast(i32, milliseconds / time.ms_per_s),
+ .tv_usec = @intCast(i32, (milliseconds % time.ms_per_s) * time.us_per_ms),
+ };
+
+ return self.setOption(os.SOL_SOCKET, os.SO_RCVTIMEO, mem.asBytes(&timeout));
+ }
+ };
+}
lib/std/x/os/socket_windows.zig
@@ -13,386 +13,438 @@ const mem = std.mem;
const windows = std.os.windows;
const ws2_32 = windows.ws2_32;
-pub const Socket = struct {
- /// Import in `Socket.Address` and `Socket.Connection`.
- pub usingnamespace @import("socket.zig").Mixin(Socket);
+pub fn Mixin(comptime Socket: type) type {
+ return struct {
+ /// Open a new socket.
+ pub fn init(domain: u32, socket_type: u32, protocol: u32) !Socket {
+ var filtered_socket_type = socket_type & ~@as(u32, os.SOCK_CLOEXEC);
+
+ var filtered_flags: u32 = ws2_32.WSA_FLAG_OVERLAPPED;
+ if (socket_type & os.SOCK_CLOEXEC != 0) {
+ filtered_flags |= ws2_32.WSA_FLAG_NO_HANDLE_INHERIT;
+ }
+
+ const fd = ws2_32.WSASocketW(
+ @intCast(i32, domain),
+ @intCast(i32, filtered_socket_type),
+ @intCast(i32, protocol),
+ null,
+ 0,
+ filtered_flags,
+ );
+ if (fd == ws2_32.INVALID_SOCKET) {
+ return switch (ws2_32.WSAGetLastError()) {
+ .WSANOTINITIALISED => {
+ _ = try windows.WSAStartup(2, 2);
+ return Socket.init(domain, socket_type, protocol);
+ },
+ .WSAEAFNOSUPPORT => error.AddressFamilyNotSupported,
+ .WSAEMFILE => error.ProcessFdQuotaExceeded,
+ .WSAENOBUFS => error.SystemResources,
+ .WSAEPROTONOSUPPORT => error.ProtocolNotSupported,
+ else => |err| windows.unexpectedWSAError(err),
+ };
+ }
+
+ return Socket{ .fd = fd };
+ }
- /// The underlying handle of a socket.
- fd: os.socket_t,
+ /// Closes the socket.
+ pub fn deinit(self: Socket) void {
+ _ = ws2_32.closesocket(self.fd);
+ }
- /// Open a new socket.
- pub fn init(domain: u32, socket_type: u32, protocol: u32) !Socket {
- var filtered_socket_type = socket_type & ~@as(u32, os.SOCK_CLOEXEC);
+ /// Shutdown either the read side, write side, or all side of the socket.
+ pub fn shutdown(self: Socket, how: os.ShutdownHow) !void {
+ const rc = ws2_32.shutdown(self.fd, switch (how) {
+ .recv => ws2_32.SD_RECEIVE,
+ .send => ws2_32.SD_SEND,
+ .both => ws2_32.SD_BOTH,
+ });
+ if (rc == ws2_32.SOCKET_ERROR) {
+ return switch (ws2_32.WSAGetLastError()) {
+ .WSAECONNABORTED => return error.ConnectionAborted,
+ .WSAECONNRESET => return error.ConnectionResetByPeer,
+ .WSAEINPROGRESS => return error.BlockingOperationInProgress,
+ .WSAEINVAL => unreachable,
+ .WSAENETDOWN => return error.NetworkSubsystemFailed,
+ .WSAENOTCONN => return error.SocketNotConnected,
+ .WSAENOTSOCK => unreachable,
+ .WSANOTINITIALISED => unreachable,
+ else => |err| return windows.unexpectedWSAError(err),
+ };
+ }
+ }
- var filtered_flags: u32 = ws2_32.WSA_FLAG_OVERLAPPED;
- if (socket_type & os.SOCK_CLOEXEC != 0) {
- filtered_flags |= ws2_32.WSA_FLAG_NO_HANDLE_INHERIT;
+ /// Binds the socket to an address.
+ pub fn bind(self: Socket, address: Socket.Address) !void {
+ const rc = ws2_32.bind(self.fd, @ptrCast(*const ws2_32.sockaddr, &address.toNative()), @intCast(c_int, address.getNativeSize()));
+ if (rc == ws2_32.SOCKET_ERROR) {
+ return switch (ws2_32.WSAGetLastError()) {
+ .WSAENETDOWN => error.NetworkSubsystemFailed,
+ .WSAEACCES => error.AccessDenied,
+ .WSAEADDRINUSE => error.AddressInUse,
+ .WSAEADDRNOTAVAIL => error.AddressNotAvailable,
+ .WSAEFAULT => error.BadAddress,
+ .WSAEINPROGRESS => error.WouldBlock,
+ .WSAEINVAL => error.AlreadyBound,
+ .WSAENOBUFS => error.NoEphemeralPortsAvailable,
+ .WSAENOTSOCK => error.NotASocket,
+ else => |err| windows.unexpectedWSAError(err),
+ };
+ }
}
- const fd = ws2_32.WSASocketW(
- @intCast(i32, domain),
- @intCast(i32, filtered_socket_type),
- @intCast(i32, protocol),
- null,
- 0,
- filtered_flags,
- );
- if (fd == ws2_32.INVALID_SOCKET) {
- return switch (ws2_32.WSAGetLastError()) {
- .WSANOTINITIALISED => {
- _ = try windows.WSAStartup(2, 2);
- return Socket.init(domain, socket_type, protocol);
- },
- .WSAEAFNOSUPPORT => error.AddressFamilyNotSupported,
- .WSAEMFILE => error.ProcessFdQuotaExceeded,
- .WSAENOBUFS => error.SystemResources,
- .WSAEPROTONOSUPPORT => error.ProtocolNotSupported,
- else => |err| windows.unexpectedWSAError(err),
- };
+ /// Start listening for incoming connections on the socket.
+ pub fn listen(self: Socket, max_backlog_size: u31) !void {
+ const rc = ws2_32.listen(self.fd, max_backlog_size);
+ if (rc == ws2_32.SOCKET_ERROR) {
+ return switch (ws2_32.WSAGetLastError()) {
+ .WSAENETDOWN => error.NetworkSubsystemFailed,
+ .WSAEADDRINUSE => error.AddressInUse,
+ .WSAEISCONN => error.AlreadyConnected,
+ .WSAEINVAL => error.SocketNotBound,
+ .WSAEMFILE, .WSAENOBUFS => error.SystemResources,
+ .WSAENOTSOCK => error.FileDescriptorNotASocket,
+ .WSAEOPNOTSUPP => error.OperationNotSupported,
+ .WSAEINPROGRESS => error.WouldBlock,
+ else => |err| windows.unexpectedWSAError(err),
+ };
+ }
}
- return Socket{ .fd = fd };
- }
-
- /// Enclose a socket abstraction over an existing socket file descriptor.
- pub fn from(fd: os.socket_t) Socket {
- return Socket{ .fd = fd };
- }
-
- /// Closes the socket.
- pub fn deinit(self: Socket) void {
- _ = ws2_32.closesocket(self.fd);
- }
-
- /// Shutdown either the read side, write side, or all side of the socket.
- pub fn shutdown(self: Socket, how: os.ShutdownHow) !void {
- const rc = ws2_32.shutdown(self.fd, switch (how) {
- .recv => ws2_32.SD_RECEIVE,
- .send => ws2_32.SD_SEND,
- .both => ws2_32.SD_BOTH,
- });
- if (rc == ws2_32.SOCKET_ERROR) {
- return switch (ws2_32.WSAGetLastError()) {
- .WSAECONNABORTED => return error.ConnectionAborted,
- .WSAECONNRESET => return error.ConnectionResetByPeer,
- .WSAEINPROGRESS => return error.BlockingOperationInProgress,
- .WSAEINVAL => unreachable,
- .WSAENETDOWN => return error.NetworkSubsystemFailed,
- .WSAENOTCONN => return error.SocketNotConnected,
- .WSAENOTSOCK => unreachable,
- .WSANOTINITIALISED => unreachable,
- else => |err| return windows.unexpectedWSAError(err),
- };
+ /// Have the socket attempt to the connect to an address.
+ pub fn connect(self: Socket, address: Socket.Address) !void {
+ const rc = ws2_32.connect(self.fd, @ptrCast(*const ws2_32.sockaddr, &address.toNative()), @intCast(c_int, address.getNativeSize()));
+ if (rc == ws2_32.SOCKET_ERROR) {
+ return switch (ws2_32.WSAGetLastError()) {
+ .WSAEADDRINUSE => error.AddressInUse,
+ .WSAEADDRNOTAVAIL => error.AddressNotAvailable,
+ .WSAECONNREFUSED => error.ConnectionRefused,
+ .WSAETIMEDOUT => error.ConnectionTimedOut,
+ .WSAEFAULT => error.BadAddress,
+ .WSAEINVAL => error.ListeningSocket,
+ .WSAEISCONN => error.AlreadyConnected,
+ .WSAENOTSOCK => error.NotASocket,
+ .WSAEACCES => error.BroadcastNotEnabled,
+ .WSAENOBUFS => error.SystemResources,
+ .WSAEAFNOSUPPORT => error.AddressFamilyNotSupported,
+ .WSAEINPROGRESS, .WSAEWOULDBLOCK => error.WouldBlock,
+ .WSAEHOSTUNREACH, .WSAENETUNREACH => error.NetworkUnreachable,
+ else => |err| windows.unexpectedWSAError(err),
+ };
+ }
}
- }
-
- /// Binds the socket to an address.
- pub fn bind(self: Socket, address: Socket.Address) !void {
- const rc = ws2_32.bind(self.fd, @ptrCast(*const ws2_32.sockaddr, &address.toNative()), @intCast(c_int, address.getNativeSize()));
- if (rc == ws2_32.SOCKET_ERROR) {
- return switch (ws2_32.WSAGetLastError()) {
- .WSAENETDOWN => error.NetworkSubsystemFailed,
- .WSAEACCES => error.AccessDenied,
- .WSAEADDRINUSE => error.AddressInUse,
- .WSAEADDRNOTAVAIL => error.AddressNotAvailable,
- .WSAEFAULT => error.BadAddress,
- .WSAEINPROGRESS => error.WouldBlock,
- .WSAEINVAL => error.AlreadyBound,
- .WSAENOBUFS => error.NoEphemeralPortsAvailable,
- .WSAENOTSOCK => error.NotASocket,
- else => |err| windows.unexpectedWSAError(err),
- };
+
+ /// Accept a pending incoming connection queued to the kernel backlog
+ /// of the socket.
+ pub fn accept(self: Socket, flags: u32) !Socket.Connection {
+ var address: ws2_32.sockaddr_storage = undefined;
+ var address_len: c_int = @sizeOf(ws2_32.sockaddr_storage);
+
+ const rc = ws2_32.accept(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len);
+ if (rc == ws2_32.INVALID_SOCKET) {
+ return switch (ws2_32.WSAGetLastError()) {
+ .WSANOTINITIALISED => unreachable,
+ .WSAECONNRESET => error.ConnectionResetByPeer,
+ .WSAEFAULT => unreachable,
+ .WSAEINVAL => error.SocketNotListening,
+ .WSAEMFILE => error.ProcessFdQuotaExceeded,
+ .WSAENETDOWN => error.NetworkSubsystemFailed,
+ .WSAENOBUFS => error.FileDescriptorNotASocket,
+ .WSAEOPNOTSUPP => error.OperationNotSupported,
+ .WSAEWOULDBLOCK => error.WouldBlock,
+ else => |err| windows.unexpectedWSAError(err),
+ };
+ }
+
+ const socket = Socket.from(rc);
+ const socket_address = Socket.Address.fromNative(@ptrCast(*ws2_32.sockaddr, &address));
+
+ return Socket.Connection.from(socket, socket_address);
}
- }
-
- /// Start listening for incoming connections on the socket.
- pub fn listen(self: Socket, max_backlog_size: u31) !void {
- const rc = ws2_32.listen(self.fd, max_backlog_size);
- if (rc == ws2_32.SOCKET_ERROR) {
- return switch (ws2_32.WSAGetLastError()) {
- .WSAENETDOWN => error.NetworkSubsystemFailed,
- .WSAEADDRINUSE => error.AddressInUse,
- .WSAEISCONN => error.AlreadyConnected,
- .WSAEINVAL => error.SocketNotBound,
- .WSAEMFILE, .WSAENOBUFS => error.SystemResources,
- .WSAENOTSOCK => error.FileDescriptorNotASocket,
- .WSAEOPNOTSUPP => error.OperationNotSupported,
- .WSAEINPROGRESS => error.WouldBlock,
- else => |err| windows.unexpectedWSAError(err),
- };
+
+ /// Read data from the socket into the buffer provided with a set of flags
+ /// specified. It returns the number of bytes read into the buffer provided.
+ pub fn read(self: Socket, buf: []u8, flags: u32) !usize {
+ var bufs = &[_]ws2_32.WSABUF{.{ .len = @intCast(u32, buf.len), .buf = buf.ptr }};
+ var num_bytes: u32 = undefined;
+ var flags_ = flags;
+
+ const rc = ws2_32.WSARecv(self.fd, bufs, 1, &num_bytes, &flags_, null, null);
+ if (rc == ws2_32.SOCKET_ERROR) {
+ return switch (ws2_32.WSAGetLastError()) {
+ .WSAECONNABORTED => error.ConnectionAborted,
+ .WSAECONNRESET => error.ConnectionResetByPeer,
+ .WSAEDISCON => error.ConnectionClosedByPeer,
+ .WSAEFAULT => error.BadBuffer,
+ .WSAEINPROGRESS,
+ .WSAEWOULDBLOCK,
+ .WSA_IO_PENDING,
+ .WSAETIMEDOUT,
+ => error.WouldBlock,
+ .WSAEINTR => error.Cancelled,
+ .WSAEINVAL => error.SocketNotBound,
+ .WSAEMSGSIZE => error.MessageTooLarge,
+ .WSAENETDOWN => error.NetworkSubsystemFailed,
+ .WSAENETRESET => error.NetworkReset,
+ .WSAENOTCONN => error.SocketNotConnected,
+ .WSAENOTSOCK => error.FileDescriptorNotASocket,
+ .WSAEOPNOTSUPP => error.OperationNotSupported,
+ .WSAESHUTDOWN => error.AlreadyShutdown,
+ .WSA_OPERATION_ABORTED => error.OperationAborted,
+ else => |err| windows.unexpectedWSAError(err),
+ };
+ }
+
+ return @intCast(usize, num_bytes);
}
- }
-
- /// Have the socket attempt to the connect to an address.
- pub fn connect(self: Socket, address: Socket.Address) !void {
- const rc = ws2_32.connect(self.fd, @ptrCast(*const ws2_32.sockaddr, &address.toNative()), @intCast(c_int, address.getNativeSize()));
- if (rc == ws2_32.SOCKET_ERROR) {
- return switch (ws2_32.WSAGetLastError()) {
- .WSAEADDRINUSE => error.AddressInUse,
- .WSAEADDRNOTAVAIL => error.AddressNotAvailable,
- .WSAECONNREFUSED => error.ConnectionRefused,
- .WSAETIMEDOUT => error.ConnectionTimedOut,
- .WSAEFAULT => error.BadAddress,
- .WSAEINVAL => error.ListeningSocket,
- .WSAEISCONN => error.AlreadyConnected,
- .WSAENOTSOCK => error.NotASocket,
- .WSAEACCES => error.BroadcastNotEnabled,
- .WSAENOBUFS => error.SystemResources,
- .WSAEAFNOSUPPORT => error.AddressFamilyNotSupported,
- .WSAEINPROGRESS, .WSAEWOULDBLOCK => error.WouldBlock,
- .WSAEHOSTUNREACH, .WSAENETUNREACH => error.NetworkUnreachable,
- else => |err| windows.unexpectedWSAError(err),
- };
+
+ /// Write a buffer of data provided to the socket with a set of flags specified.
+ /// It returns the number of bytes that are written to the socket.
+ pub fn write(self: Socket, buf: []const u8, flags: u32) !usize {
+ var bufs = &[_]ws2_32.WSABUF{.{ .len = @intCast(u32, buf.len), .buf = @intToPtr([*]u8, @ptrToInt(buf.ptr)) }};
+ var num_bytes: u32 = undefined;
+
+ const rc = ws2_32.WSASend(self.fd, bufs, 1, &num_bytes, flags, null, null);
+ if (rc == ws2_32.SOCKET_ERROR) {
+ return switch (ws2_32.WSAGetLastError()) {
+ .WSAECONNABORTED => error.ConnectionAborted,
+ .WSAECONNRESET => error.ConnectionResetByPeer,
+ .WSAEFAULT => error.BadBuffer,
+ .WSAEINPROGRESS,
+ .WSAEWOULDBLOCK,
+ .WSA_IO_PENDING,
+ .WSAETIMEDOUT,
+ => error.WouldBlock,
+ .WSAEINTR => error.Cancelled,
+ .WSAEINVAL => error.SocketNotBound,
+ .WSAEMSGSIZE => error.MessageTooLarge,
+ .WSAENETDOWN => error.NetworkSubsystemFailed,
+ .WSAENETRESET => error.NetworkReset,
+ .WSAENOBUFS => error.BufferDeadlock,
+ .WSAENOTCONN => error.SocketNotConnected,
+ .WSAENOTSOCK => error.FileDescriptorNotASocket,
+ .WSAEOPNOTSUPP => error.OperationNotSupported,
+ .WSAESHUTDOWN => error.AlreadyShutdown,
+ .WSA_OPERATION_ABORTED => error.OperationAborted,
+ else => |err| windows.unexpectedWSAError(err),
+ };
+ }
+
+ return @intCast(usize, num_bytes);
}
- }
-
- /// Accept a pending incoming connection queued to the kernel backlog
- /// of the socket.
- pub fn accept(self: Socket, flags: u32) !Socket.Connection {
- var address: ws2_32.sockaddr_storage = undefined;
- var address_len: c_int = @sizeOf(ws2_32.sockaddr_storage);
-
- const rc = ws2_32.accept(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len);
- if (rc == ws2_32.INVALID_SOCKET) {
- return switch (ws2_32.WSAGetLastError()) {
- .WSANOTINITIALISED => unreachable,
- .WSAECONNRESET => error.ConnectionResetByPeer,
- .WSAEFAULT => unreachable,
- .WSAEINVAL => error.SocketNotListening,
- .WSAEMFILE => error.ProcessFdQuotaExceeded,
- .WSAENETDOWN => error.NetworkSubsystemFailed,
- .WSAENOBUFS => error.FileDescriptorNotASocket,
- .WSAEOPNOTSUPP => error.OperationNotSupported,
- .WSAEWOULDBLOCK => error.WouldBlock,
- else => |err| windows.unexpectedWSAError(err),
- };
+
+ /// Writes multiple I/O vectors with a prepended message header to the socket
+ /// with a set of flags specified. It returns the number of bytes that are
+ /// written to the socket.
+ pub fn writeVectorized(self: Socket, msg: ws2_32.msghdr_const, flags: u32) !usize {
+ const call = try windows.loadWinsockExtensionFunction(ws2_32.LPFN_WSASENDMSG, self.fd, ws2_32.WSAID_WSASENDMSG);
+
+ var num_bytes: u32 = undefined;
+
+ const rc = call(self.fd, &msg, flags, &num_bytes, null, null);
+ if (rc == ws2_32.SOCKET_ERROR) {
+ return switch (ws2_32.WSAGetLastError()) {
+ .WSAECONNABORTED => error.ConnectionAborted,
+ .WSAECONNRESET => error.ConnectionResetByPeer,
+ .WSAEFAULT => error.BadBuffer,
+ .WSAEINPROGRESS,
+ .WSAEWOULDBLOCK,
+ .WSA_IO_PENDING,
+ .WSAETIMEDOUT,
+ => error.WouldBlock,
+ .WSAEINTR => error.Cancelled,
+ .WSAEINVAL => error.SocketNotBound,
+ .WSAEMSGSIZE => error.MessageTooLarge,
+ .WSAENETDOWN => error.NetworkSubsystemFailed,
+ .WSAENETRESET => error.NetworkReset,
+ .WSAENOBUFS => error.BufferDeadlock,
+ .WSAENOTCONN => error.SocketNotConnected,
+ .WSAENOTSOCK => error.FileDescriptorNotASocket,
+ .WSAEOPNOTSUPP => error.OperationNotSupported,
+ .WSAESHUTDOWN => error.AlreadyShutdown,
+ .WSA_OPERATION_ABORTED => error.OperationAborted,
+ else => |err| windows.unexpectedWSAError(err),
+ };
+ }
+
+ return @intCast(usize, num_bytes);
}
- const socket = Socket.from(rc);
- const socket_address = Socket.Address.fromNative(@ptrCast(*ws2_32.sockaddr, &address));
-
- return Socket.Connection.from(socket, socket_address);
- }
-
- /// Read data from the socket into the buffer provided with a set of flags
- /// specified. It returns the number of bytes read into the buffer provided.
- pub fn read(self: Socket, buf: []u8, flags: u32) !usize {
- var bufs = &[_]ws2_32.WSABUF{.{ .len = @intCast(u32, buf.len), .buf = buf.ptr }};
- var flags_ = flags;
-
- const rc = ws2_32.WSARecv(self.fd, bufs, 1, null, &flags_, null, null);
- if (rc == ws2_32.SOCKET_ERROR) {
- return switch (ws2_32.WSAGetLastError()) {
- .WSAECONNABORTED => error.ConnectionAborted,
- .WSAECONNRESET => error.ConnectionResetByPeer,
- .WSAEDISCON => error.ConnectionClosedByPeer,
- .WSAEFAULT => error.BadBuffer,
- .WSAEINPROGRESS,
- .WSAEWOULDBLOCK,
- .WSA_IO_PENDING,
- .WSAETIMEDOUT,
- => error.WouldBlock,
- .WSAEINTR => error.Cancelled,
- .WSAEINVAL => error.SocketNotBound,
- .WSAEMSGSIZE => error.MessageTooLarge,
- .WSAENETDOWN => error.NetworkSubsystemFailed,
- .WSAENETRESET => error.NetworkReset,
- .WSAENOTCONN => error.SocketNotConnected,
- .WSAENOTSOCK => error.FileDescriptorNotASocket,
- .WSAEOPNOTSUPP => error.OperationNotSupported,
- .WSAESHUTDOWN => error.AlreadyShutdown,
- .WSA_OPERATION_ABORTED => error.OperationAborted,
- else => |err| windows.unexpectedWSAError(err),
- };
+ /// Read multiple I/O vectors with a prepended message header from the socket
+ /// with a set of flags specified. It returns the number of bytes that were
+ /// read into the buffer provided.
+ pub fn readVectorized(self: Socket, msg: *ws2_32.msghdr, flags: u32) !usize {
+ const call = try windows.loadWinsockExtensionFunction(ws2_32.LPFN_WSARECVMSG, self.fd, ws2_32.WSAID_WSARECVMSG);
+
+ var num_bytes: u32 = undefined;
+
+ const rc = call(self.fd, msg, &num_bytes, null, null);
+ if (rc == ws2_32.SOCKET_ERROR) {
+ return switch (ws2_32.WSAGetLastError()) {
+ .WSAECONNABORTED => error.ConnectionAborted,
+ .WSAECONNRESET => error.ConnectionResetByPeer,
+ .WSAEDISCON => error.ConnectionClosedByPeer,
+ .WSAEFAULT => error.BadBuffer,
+ .WSAEINPROGRESS,
+ .WSAEWOULDBLOCK,
+ .WSA_IO_PENDING,
+ .WSAETIMEDOUT,
+ => error.WouldBlock,
+ .WSAEINTR => error.Cancelled,
+ .WSAEINVAL => error.SocketNotBound,
+ .WSAEMSGSIZE => error.MessageTooLarge,
+ .WSAENETDOWN => error.NetworkSubsystemFailed,
+ .WSAENETRESET => error.NetworkReset,
+ .WSAENOTCONN => error.SocketNotConnected,
+ .WSAENOTSOCK => error.FileDescriptorNotASocket,
+ .WSAEOPNOTSUPP => error.OperationNotSupported,
+ .WSAESHUTDOWN => error.AlreadyShutdown,
+ .WSA_OPERATION_ABORTED => error.OperationAborted,
+ else => |err| windows.unexpectedWSAError(err),
+ };
+ }
+
+ return @intCast(usize, num_bytes);
}
- return @intCast(usize, rc);
- }
-
- /// Write a buffer of data provided to the socket with a set of flags specified.
- /// It returns the number of bytes that are written to the socket.
- pub fn write(self: Socket, buf: []const u8, flags: u32) !usize {
- var bufs = &[_]ws2_32.WSABUF{.{ .len = @intCast(u32, buf.len), .buf = buf.ptr }};
- var flags_ = flags;
-
- const rc = ws2_32.WSASend(self.fd, bufs, 1, null, &flags_, null, null);
- if (rc == ws2_32.SOCKET_ERROR) {
- return switch (ws2_32.WSAGetLastError()) {
- .WSAECONNABORTED => error.ConnectionAborted,
- .WSAECONNRESET => error.ConnectionResetByPeer,
- .WSAEFAULT => error.BadBuffer,
- .WSAEINPROGRESS,
- .WSAEWOULDBLOCK,
- .WSA_IO_PENDING,
- .WSAETIMEDOUT,
- => error.WouldBlock,
- .WSAEINTR => error.Cancelled,
- .WSAEINVAL => error.SocketNotBound,
- .WSAEMSGSIZE => error.MessageTooLarge,
- .WSAENETDOWN => error.NetworkSubsystemFailed,
- .WSAENETRESET => error.NetworkReset,
- .WSAENOBUFS => error.BufferDeadlock,
- .WSAENOTCONN => error.SocketNotConnected,
- .WSAENOTSOCK => error.FileDescriptorNotASocket,
- .WSAEOPNOTSUPP => error.OperationNotSupported,
- .WSAESHUTDOWN => error.AlreadyShutdown,
- .WSA_OPERATION_ABORTED => error.OperationAborted,
- else => |err| windows.unexpectedWSAError(err),
- };
+ /// Query the address that the socket is locally bounded to.
+ pub fn getLocalAddress(self: Socket) !Socket.Address {
+ var address: ws2_32.sockaddr_storage = undefined;
+ var address_len: c_int = @sizeOf(ws2_32.sockaddr_storage);
+
+ const rc = ws2_32.getsockname(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len);
+ if (rc == ws2_32.SOCKET_ERROR) {
+ return switch (ws2_32.WSAGetLastError()) {
+ .WSANOTINITIALISED => unreachable,
+ .WSAEFAULT => unreachable,
+ .WSAENETDOWN => error.NetworkSubsystemFailed,
+ .WSAENOTSOCK => error.FileDescriptorNotASocket,
+ .WSAEINVAL => error.SocketNotBound,
+ else => |err| windows.unexpectedWSAError(err),
+ };
+ }
+
+ return Socket.Address.fromNative(@ptrCast(*ws2_32.sockaddr, &address));
}
- return @intCast(usize, rc);
- }
-
- /// Writes multiple I/O vectors with a prepended message header to the socket
- /// with a set of flags specified. It returns the number of bytes that are
- /// written to the socket.
- pub fn writeVectorized(self: Socket, msg: os.msghdr_const, flags: u32) !usize {
- return error.NotImplemented;
- }
-
- /// Read multiple I/O vectors with a prepended message header from the socket
- /// with a set of flags specified. It returns the number of bytes that were
- /// read into the buffer provided.
- pub fn readVectorized(self: Socket, msg: *os.msghdr, flags: u32) !usize {
- return error.NotImplemented;
- }
-
- /// Query the address that the socket is locally bounded to.
- pub fn getLocalAddress(self: Socket) !Socket.Address {
- var address: ws2_32.sockaddr_storage = undefined;
- var address_len: c_int = @sizeOf(ws2_32.sockaddr_storage);
-
- const rc = ws2_32.getsockname(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len);
- if (rc == ws2_32.SOCKET_ERROR) {
- return switch (ws2_32.WSAGetLastError()) {
- .WSANOTINITIALISED => unreachable,
- .WSAEFAULT => unreachable,
- .WSAENETDOWN => error.NetworkSubsystemFailed,
- .WSAENOTSOCK => error.FileDescriptorNotASocket,
- .WSAEINVAL => error.SocketNotBound,
- else => |err| windows.unexpectedWSAError(err),
- };
+ /// Query the address that the socket is connected to.
+ pub fn getRemoteAddress(self: Socket) !Socket.Address {
+ var address: ws2_32.sockaddr_storage = undefined;
+ var address_len: c_int = @sizeOf(ws2_32.sockaddr_storage);
+
+ const rc = ws2_32.getpeername(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len);
+ if (rc == ws2_32.SOCKET_ERROR) {
+ return switch (ws2_32.WSAGetLastError()) {
+ .WSANOTINITIALISED => unreachable,
+ .WSAEFAULT => unreachable,
+ .WSAENETDOWN => error.NetworkSubsystemFailed,
+ .WSAENOTSOCK => error.FileDescriptorNotASocket,
+ .WSAEINVAL => error.SocketNotBound,
+ else => |err| windows.unexpectedWSAError(err),
+ };
+ }
+
+ return Socket.Address.fromNative(@ptrCast(*ws2_32.sockaddr, &address));
}
- return Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address));
- }
-
- /// Query the address that the socket is connected to.
- pub fn getRemoteAddress(self: Socket) !Socket.Address {
- var address: ws2_32.sockaddr_storage = undefined;
- var address_len: c_int = @sizeOf(ws2_32.sockaddr_storage);
-
- const rc = ws2_32.getpeername(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len);
- if (rc == ws2_32.SOCKET_ERROR) {
- return switch (ws2_32.WSAGetLastError()) {
- .WSANOTINITIALISED => unreachable,
- .WSAEFAULT => unreachable,
- .WSAENETDOWN => error.NetworkSubsystemFailed,
- .WSAENOTSOCK => error.FileDescriptorNotASocket,
- .WSAEINVAL => error.SocketNotBound,
- else => |err| windows.unexpectedWSAError(err),
- };
+ /// Query and return the latest cached error on the socket.
+ pub fn getError(self: Socket) !void {
+ return {};
+ }
+
+ /// Query the read buffer size of the socket.
+ pub fn getReadBufferSize(self: Socket) !u32 {
+ return 0;
}
- return Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address));
- }
-
- /// Query and return the latest cached error on the socket.
- pub fn getError(self: Socket) !void {
- return {};
- }
-
- /// Query the read buffer size of the socket.
- pub fn getReadBufferSize(self: Socket) !u32 {
- return 0;
- }
-
- /// Query the write buffer size of the socket.
- pub fn getWriteBufferSize(self: Socket) !u32 {
- return 0;
- }
-
- /// Set a socket option.
- pub fn setOption(self: Socket, level: u32, code: u32, value: []const u8) !void {
- const rc = ws2_32.setsockopt(self.fd, @intCast(i32, level), @intCast(i32, code), value.ptr, @intCast(i32, value.len));
- if (rc == ws2_32.SOCKET_ERROR) {
- return switch (ws2_32.WSAGetLastError()) {
- .WSANOTINITIALISED => unreachable,
- .WSAENETDOWN => return error.NetworkSubsystemFailed,
- .WSAEFAULT => unreachable,
- .WSAENOTSOCK => return error.FileDescriptorNotASocket,
- .WSAEINVAL => return error.SocketNotBound,
- .WSAENOTCONN => return error.SocketNotConnected,
- .WSAESHUTDOWN => return error.AlreadyShutdown,
- else => |err| windows.unexpectedWSAError(err),
+ /// Query the write buffer size of the socket.
+ pub fn getWriteBufferSize(self: Socket) !u32 {
+ return 0;
+ }
+
+ /// Set a socket option.
+ pub fn setOption(self: Socket, level: u32, code: u32, value: []const u8) !void {
+ const rc = ws2_32.setsockopt(self.fd, @intCast(i32, level), @intCast(i32, code), value.ptr, @intCast(i32, value.len));
+ if (rc == ws2_32.SOCKET_ERROR) {
+ return switch (ws2_32.WSAGetLastError()) {
+ .WSANOTINITIALISED => unreachable,
+ .WSAENETDOWN => return error.NetworkSubsystemFailed,
+ .WSAEFAULT => unreachable,
+ .WSAENOTSOCK => return error.FileDescriptorNotASocket,
+ .WSAEINVAL => return error.SocketNotBound,
+ .WSAENOTCONN => return error.SocketNotConnected,
+ .WSAESHUTDOWN => return error.AlreadyShutdown,
+ else => |err| windows.unexpectedWSAError(err),
+ };
+ }
+ }
+
+ /// Have close() or shutdown() syscalls block until all queued messages in the socket have been successfully
+ /// sent, or if the timeout specified in seconds has been reached. It returns `error.UnsupportedSocketOption`
+ /// if the host does not support the option for a socket to linger around up until a timeout specified in
+ /// seconds.
+ pub fn setLinger(self: Socket, timeout_seconds: ?u16) !void {
+ const settings = ws2_32.linger{
+ .l_onoff = @as(u16, @boolToInt(timeout_seconds != null)),
+ .l_linger = if (timeout_seconds) |seconds| seconds else 0,
};
+
+ return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_LINGER, mem.asBytes(&settings));
+ }
+
+ /// On connection-oriented sockets, have keep-alive messages be sent periodically. The timing in which keep-alive
+ /// messages are sent are dependant on operating system settings. It returns `error.UnsupportedSocketOption` if
+ /// the host does not support periodically sending keep-alive messages on connection-oriented sockets.
+ pub fn setKeepAlive(self: Socket, enabled: bool) !void {
+ return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_KEEPALIVE, mem.asBytes(&@as(u32, @boolToInt(enabled))));
+ }
+
+ /// Allow multiple sockets on the same host to listen on the same address. It returns `error.UnsupportedSocketOption` if
+ /// the host does not support sockets listening the same address.
+ pub fn setReuseAddress(self: Socket, enabled: bool) !void {
+ return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_REUSEADDR, mem.asBytes(&@as(u32, @boolToInt(enabled))));
+ }
+
+ /// Allow multiple sockets on the same host to listen on the same port. It returns `error.UnsupportedSocketOption` if
+ /// the host does not supports sockets listening on the same port.
+ ///
+ /// TODO: verify if this truly mimicks SO_REUSEPORT behavior, or if SO_REUSE_UNICASTPORT provides the correct behavior
+ pub fn setReusePort(self: Socket, enabled: bool) !void {
+ try self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_BROADCAST, mem.asBytes(&@as(u32, @boolToInt(enabled))));
+ try self.setReuseAddress(enabled);
+ }
+
+ /// Set the write buffer size of the socket.
+ pub fn setWriteBufferSize(self: Socket, size: u32) !void {
+ return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_SNDBUF, mem.asBytes(&size));
+ }
+
+ /// Set the read buffer size of the socket.
+ pub fn setReadBufferSize(self: Socket, size: u32) !void {
+ return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_RCVBUF, mem.asBytes(&size));
+ }
+
+ /// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is
+ /// set on a non-blocking socket.
+ ///
+ /// Set a timeout on the socket that is to occur if no messages are successfully written
+ /// to its bound destination after a specified number of milliseconds. A subsequent write
+ /// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded.
+ pub fn setWriteTimeout(self: Socket, milliseconds: u32) !void {
+ return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_SNDTIMEO, mem.asBytes(&milliseconds));
+ }
+
+ /// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is
+ /// set on a non-blocking socket.
+ ///
+ /// Set a timeout on the socket that is to occur if no messages are successfully read
+ /// from its bound destination after a specified number of milliseconds. A subsequent
+ /// read from the socket will thereafter return `error.WouldBlock` should the timeout be
+ /// exceeded.
+ pub fn setReadTimeout(self: Socket, milliseconds: u32) !void {
+ return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_RCVTIMEO, mem.asBytes(&milliseconds));
}
- }
-
- /// Have close() or shutdown() syscalls block until all queued messages in the socket have been successfully
- /// sent, or if the timeout specified in seconds has been reached. It returns `error.UnsupportedSocketOption`
- /// if the host does not support the option for a socket to linger around up until a timeout specified in
- /// seconds.
- pub fn setLinger(self: Socket, timeout_seconds: ?u16) !void {
- const settings = ws2_32.linger{
- .l_onoff = @as(u16, @boolToInt(timeout_seconds != null)),
- .l_linger = if (timeout_seconds) |seconds| seconds else 0,
- };
-
- return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_LINGER, mem.asBytes(&settings));
- }
-
- /// On connection-oriented sockets, have keep-alive messages be sent periodically. The timing in which keep-alive
- /// messages are sent are dependant on operating system settings. It returns `error.UnsupportedSocketOption` if
- /// the host does not support periodically sending keep-alive messages on connection-oriented sockets.
- pub fn setKeepAlive(self: Socket, enabled: bool) !void {
- return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_KEEPALIVE, mem.asBytes(&@as(u32, @boolToInt(enabled))));
- }
-
- /// Allow multiple sockets on the same host to listen on the same address. It returns `error.UnsupportedSocketOption` if
- /// the host does not support sockets listening the same address.
- pub fn setReuseAddress(self: Socket, enabled: bool) !void {
- return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_REUSEADDR, mem.asBytes(&@as(u32, @boolToInt(enabled))));
- }
-
- /// Allow multiple sockets on the same host to listen on the same port. It returns `error.UnsupportedSocketOption` if
- /// the host does not supports sockets listening on the same port.
- ///
- /// TODO: verify if this truly mimicks SO_REUSEPORT behavior, or if SO_REUSE_UNICASTPORT provides the correct behavior
- pub fn setReusePort(self: Socket, enabled: bool) !void {
- try self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_BROADCAST, mem.asBytes(&@as(u32, @boolToInt(enabled))));
- try self.setReuseAddress(enabled);
- }
-
- /// Set the write buffer size of the socket.
- pub fn setWriteBufferSize(self: Socket, size: u32) !void {
- return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_SNDBUF, mem.asBytes(&size));
- }
-
- /// Set the read buffer size of the socket.
- pub fn setReadBufferSize(self: Socket, size: u32) !void {
- return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_RCVBUF, mem.asBytes(&size));
- }
-
- /// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is
- /// set on a non-blocking socket.
- ///
- /// Set a timeout on the socket that is to occur if no messages are successfully written
- /// to its bound destination after a specified number of milliseconds. A subsequent write
- /// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded.
- pub fn setWriteTimeout(self: Socket, milliseconds: u32) !void {
- return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_SNDTIMEO, mem.asBytes(&milliseconds));
- }
-
- /// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is
- /// set on a non-blocking socket.
- ///
- /// Set a timeout on the socket that is to occur if no messages are successfully read
- /// from its bound destination after a specified number of milliseconds. A subsequent
- /// read from the socket will thereafter return `error.WouldBlock` should the timeout be
- /// exceeded.
- pub fn setReadTimeout(self: Socket, milliseconds: u32) !void {
- return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_RCVTIMEO, mem.asBytes(&milliseconds));
- }
-};
+ };
+}