js/net/socket

js/net/socket.ts

fino:socket — POSIX socket API for TCP, UDP, and Unix domain sockets.

This module wraps the POSIX socket syscalls via fino:ffi and provides both a low-level procedural API (raw fd integers) and a higher-level Socket class with async connect() / listen() and a split() method that divides a connection into independent Reader and Writer halves (from fino:stream).

Address families and address objects

Addresses are plain JS objects rather than classes. The family field selects the address type: { family: 'ipv4', ip: '127.0.0.1', port: 8080 } { family: 'ipv6', ip: '::1', port: 8080, scopeId: 0 } { family: 'unix', path: '/tmp/app.sock' }

encodeAddr() translates these to C sockaddr_in / sockaddr_in6 / sockaddr_un buffers using inet_pton(3) for IP parsing. decodeAddr() is the reverse, using inet_ntop(3) for IP formatting.

Platform differences in sockaddr layout

macOS struct sockaddr includes a sa_len byte at offset 0: offset 0: sa_len (uint8) — total size of the struct offset 1: sa_family (uint8) — AF_INET / AF_INET6 / AF_UNIX

Linux struct sockaddr has no sa_len field: offset 0: sa_family (uint16 LE) — AF_INET / AF_INET6 / AF_UNIX

writeFamily() and readFamily() handle this difference. All encodeAddr() and decodeAddr() code goes through these helpers.

Non-blocking connect

Socket.connect() uses non-blocking sockets for async connection setup: 1. Create a socket, set O_NONBLOCK. 2. Call connect() — returns immediately with EINPROGRESS. 3. await loop.writable(lp, fd) — suspend until the kernel signals that the connection attempt completed (success or failure). 4. getsockopt(fd, SOL_SOCKET, SO_ERROR) — check the actual result. Zero means success; nonzero is an errno value.

This approach never blocks the Fino process, even for connections to remote hosts that may be slow to respond.

accept4 on Linux

Linux provides accept4(2) which sets SOCK_NONBLOCK on the accepted socket atomically, avoiding a separate fcntl(F_SETFL) call. On macOS, we call accept(2) followed by setNonblocking(fd). The accept function in this module handles the platform difference automatically.

EAGAIN values differ by platform

When a non-blocking call would block, the kernel returns EAGAIN or EWOULDBLOCK (same value on most platforms). But the numeric errno differs: Linux: EAGAIN = 11 (returned as -11 from our FFI functions) macOS: EAGAIN = 35 (returned as -35)

Similarly, EINPROGRESS is -115 on Linux and -36 on macOS. The errno constants exported by this module use the correct values for the current platform.

Socket.split() and fd lifecycle

Socket.split() returns [Reader, Writer] that share the underlying fd. The fd should only be close()d when both halves are done. The onClose callbacks implement a reference-count of 2: - Reader's close: shutdown(fd, SHUT_RD) + decrement ref. This sends an EOF to any in-flight read(2) call, unblocking the Reader cleanly. - Writer's close: shutdown(fd, SHUT_WR) + decrement ref. This sends a TCP FIN to the remote peer. - When both have closed (ref reaches 0): close(fd) releases the fd.

shutdown(SHUT_RD) is a local operation — it doesn't send anything on the wire; it just makes the read side of the socket return 0 (EOF) immediately. shutdown(SHUT_WR) sends a FIN packet, signalling to the peer that we're done sending but may still be reading.

Server (Socket.listen)

Socket.listen() returns a plain object (not a class) with: - accept() — async function that awaits the next incoming connection - close() — closes the server socket - [Symbol.asyncIterator] — iterate incoming connections

The server socket is set non-blocking after listen(2), and loop.readable() is used to await the next connection before calling accept(). If accept() returns null (spurious wakeup), the loop retries.

Unix domain sockets: Socket.listen() calls unlink(path) before bind() to remove any stale socket file from a previous run.

Contributing

import { Socket } from 'fino:socket';

const socket = await Socket.connect({ family: 'ipv4', ip: '127.0.0.1', port: 8080 });
const [reader, writer] = socket.split();
await writer.write(new TextEncoder().encode('ping'));
const reply = await reader.read();

Interfaces

interface IPv4Address {

IPv4 socket address.

Used with TCP and UDP helpers. ip must be a numeric IPv4 literal accepted by inet_pton; hostnames are not resolved here.

const addr: IPv4Address = { family: 'ipv4', ip: '127.0.0.1', port: 8080 };

Properties

family: 'ipv4'

Literal IPv4 family tag.

if (addr.family === 'ipv4') console.log(addr.ip);
ip: string

Numeric IPv4 address string.

const addr = { family: 'ipv4', ip: '0.0.0.0', port: 3000 } as const;
port: number

TCP or UDP port number; encoded as an unsigned 16-bit network-order field.

console.log(addr.port);

interface IPv6Address {

IPv6 socket address.

ip must be a numeric IPv6 literal. Scope IDs are not represented in this public shape through optional scopeId.

const addr: IPv6Address = { family: 'ipv6', ip: '::1', port: 8080 };

Properties

family: 'ipv6'

Literal IPv6 family tag.

if (addr.family === 'ipv6') console.log(addr.ip);
ip: string

Numeric IPv6 address string.

const loopback = { family: 'ipv6', ip: '::1', port: 3000 } as const;
port: number

TCP or UDP port number.

console.log(addr.port);
scopeId?: number

IPv6 scope ID for link-local and interface-scoped addresses.

const addr: IPv6Address = { family: 'ipv6', ip: 'fe80::1', port: 5353, scopeId: 4 };

interface UnixAddress {

Unix domain socket address.

Paths must fit the platform sockaddr_un limit after UTF-8 encoding and a trailing NUL byte. Socket.listen() unlinks stale paths before binding.

const addr: UnixAddress = { family: 'unix', path: '/tmp/fino.sock' };

Properties

family: 'unix'

Literal Unix-domain family tag.

if (addr.family === 'unix') console.log(addr.path);
path: string

Filesystem path for the socket node.

const addr = { family: 'unix', path: '/tmp/app.sock' } as const;

interface UnknownAddress {

Address returned when the native family is not recognized by this module.

This can appear when decoding a kernel-filled sockaddr with an address family this module does not support.

const decoded = decodeAddr(raw);
if (decoded.family.startsWith('unknown')) console.log(decoded.family);

Properties

family: string

String form such as unknown(123).

console.log(addr.family);

interface ConnectOptions {

Options for high-level TCP connection setup.

const fd = await connectTcp(addr, { noDelay: true });

Properties

noDelay?: boolean

Enable TCP_NODELAY after creating the socket.

await Socket.connect(addr, { noDelay: true });

interface ListenOptions {

Options for high-level server socket setup.

Defaults are reuseAddr: true, reusePort: false, and backlog: 128 in the high-level listener.

const server = Socket.listen({ family: 'ipv4', ip: '0.0.0.0', port: 3000 }, { backlog: 256 });

Properties

reuseAddr?: boolean

Set SO_REUSEADDR before bind.

Socket.listen(addr, { reuseAddr: true });
reusePort?: boolean

Set SO_REUSEPORT before bind where supported.

Socket.listen(addr, { reusePort: true });
backlog?: number

Listen backlog passed to listen(2).

Socket.listen(addr, { backlog: 512 });

interface Server {

Server object returned by Socket.listen().

The server owns a non-blocking listening fd. accept() waits for a connection and returns null only for a spurious non-blocking wakeup; async iteration skips those nulls.

const server = Socket.listen({ family: 'ipv4', ip: '127.0.0.1', port: 0 });
for await (const conn of server) conn.close();

Properties

fd: number

Listening file descriptor.

console.log(server.fd);
address: Address

Bound address, including the assigned port when port zero was used.

console.log(server.address);

Readonly Properties

readonly closed: boolean

Whether the listening fd has been closed.

Methods

accept(): Promise<Socket | null>

Accept the next connection, or null after a spurious wakeup.

const conn = await server.accept();
if (conn !== null) conn.close();
close(): void

Close the listening fd. In-flight accepted sockets are not closed.

server.close();

interface NetworkInterface {

Network interface metadata returned by networkInterfaces().

The index value is the kernel interface index used by IPv6 scoped multicast APIs and link-local socket addresses.

const interfaces = networkInterfaces();
console.log(interfaces[0]?.index, interfaces[0]?.name);

Properties

index: number

Kernel interface index.

name: string

Interface name such as lo0 or en0.

flags?: number

Native interface flags, when supplied by callers or future platform helpers.

up?: boolean

Whether the interface is marked up, when flags are available.

loopback?: boolean

Whether the interface is marked loopback, when flags are available.

multicast?: boolean

Whether the interface supports multicast, when flags are available.

addresses?: Address[]

IPv4 and IPv6 addresses assigned to the interface, when supplied.

netmasks?: Address[]

Netmasks corresponding to addresses, when supplied.

Types

type Address = IPv4Address | IPv6Address | UnixAddress

Supported socket address shapes accepted by high-level and low-level APIs.

const addr: Address = { family: 'ipv4', ip: '127.0.0.1', port: 80 };

type SendmsgBatchPacket = { data: Uint8Array | ArrayBuffer; dest: Address; ecn?: number; }

Constants

const AF_INET

IPv4 address family constant.

const fd = socket(AF_INET, SOCK_STREAM, 0);

const AF_INET6

IPv6 address family constant.

const fd = socket(AF_INET6, SOCK_STREAM, 0);

const AF_UNIX

Unix domain socket address family constant.

const fd = socket(AF_UNIX, SOCK_STREAM, 0);

const SOCK_STREAM

Stream socket type, typically TCP.

const fd = socket(AF_INET, SOCK_STREAM, 0);

const SOCK_DGRAM

Datagram socket type, typically UDP.

const fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

const SOCK_NONBLOCK

Linux socket type flag for atomic non-blocking creation; zero elsewhere.

const type = SOCK_STREAM | SOCK_NONBLOCK;

const IPPROTO_TCP

TCP protocol number.

const fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

const IPPROTO_IP

IPv4 option level used with setsockopt.

const IPPROTO_IPV6

IPv6 option level used with setsockopt.

const IPPROTO_UDP

UDP protocol number.

const fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

const SOL_SOCKET

Socket option level for setsockopt and getsockopt.

setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, true);

const SO_REUSEADDR

Allow reusing a recently-bound local address.

setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, true);

const SO_REUSEPORT

Allow multiple listeners to share a local address where supported.

setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, true);

const SO_KEEPALIVE

Enable TCP keepalive probes.

setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, true);

const SO_RCVBUF

Receive buffer size socket option.

const SO_SNDBUF

Send buffer size socket option.

const SO_ERROR

Socket option used to read pending connection errors.

const errno = new DataView(getsockopt(fd, SOL_SOCKET, SO_ERROR)).getInt32(0, true);

const IPPROTO_TCP_LEVEL

TCP option level used with setsockopt.

setsockopt(fd, IPPROTO_TCP_LEVEL, TCP_NODELAY, true);

const TCP_NODELAY

Disable Nagle's algorithm for TCP sockets.

setsockopt(fd, IPPROTO_TCP_LEVEL, TCP_NODELAY, true);

const IP_TTL

IPv4 unicast TTL option.

const IP_ADD_MEMBERSHIP

Join an IPv4 multicast group with setsockopt(IPPROTO_IP, IP_ADD_MEMBERSHIP, ...).

const IP_DROP_MEMBERSHIP

Leave an IPv4 multicast group.

const IP_MULTICAST_IF

Select the IPv4 multicast outbound interface.

const IP_MULTICAST_TTL

Set IPv4 multicast packet TTL.

const IP_MULTICAST_LOOP

Enable or disable IPv4 multicast loopback.

const IP_TOS

IPv4 type-of-service / traffic-class option.

const IP_RECVTOS

IPv4 receive type-of-service ancillary-data option.

const IP_PKTINFO

IPv4 receive packet-info ancillary-data option.

const IP_RECVPKTINFO

IPv4 receive packet-info socket option.

const IPV6_V6ONLY

IPv6-only bind option.

const IPV6_UNICAST_HOPS

IPv6 unicast hop-limit option.

const IPV6_JOIN_GROUP

Join an IPv6 multicast group.

const IPV6_LEAVE_GROUP

Leave an IPv6 multicast group.

const IPV6_MULTICAST_IF

Select the IPv6 multicast outbound interface by index.

const IPV6_MULTICAST_HOPS

Set IPv6 multicast hop limit.

const IPV6_MULTICAST_LOOP

Enable or disable IPv6 multicast loopback.

const IPV6_RECVTCLASS

IPv6 receive traffic-class ancillary-data option.

const IPV6_TCLASS

IPv6 traffic-class option.

const IPV6_RECVPKTINFO

IPv6 receive packet-info socket option.

const IPV6_PKTINFO

IPv6 packet-info ancillary-data type.

const SHUT_RD

Shut down the read side of a socket.

shutdown(fd, SHUT_RD);

const SHUT_WR

Shut down the write side of a socket.

shutdown(fd, SHUT_WR);

const SHUT_RDWR

Shut down both sides of a socket.

shutdown(fd, SHUT_RDWR);

const EAGAIN

Negative errno returned when a non-blocking operation would block.

if (recv(fd) === EAGAIN) await loop.readable(fd);

const EINPROGRESS

Negative errno returned while a non-blocking connect is in progress.

if (connect(fd, addr) === EINPROGRESS) await loop.writable(fd);

const ECONNRESET

Negative errno returned when a peer resets the connection.

if (err === ECONNRESET) console.log('peer reset');

const EPIPE

Negative errno returned when writing to a closed pipe/socket.

if (send(fd, bytes) === EPIPE) console.log('closed');

const EADDRINUSE

Negative errno returned when a local address is already in use.

if (rc === EADDRINUSE) console.log('address busy');

const ECONNREFUSED

Negative errno returned when a remote endpoint refuses a connection.

if (rc === ECONNREFUSED) console.log('refused');

Functions

function encodeAddr(addr: Address): { buf: ArrayBuffer; len: number; }

Encode a JS socket address into a native sockaddr buffer and byte length.

Throws when IP literals fail inet_pton, Unix socket paths exceed the native limit, or the address family is unknown. The returned buffer is ready for bind, connect, or sendto.

const { buf, len } = encodeAddr({ family: 'ipv4', ip: '127.0.0.1', port: 80 });

function decodeAddr(buf: ArrayBuffer): Address | UnknownAddress

Decode a sockaddr ArrayBuffer into a JS address object.

Unknown native address families return { family: 'unknown(n)' } instead of throwing. IP addresses are formatted via inet_ntop.

const addr = decodeAddr(sockaddrBuffer);
console.log(addr.family);

function socket(family: number = AF_INET, type: number = SOCK_STREAM, protocol: number = 0): number

Create a socket.

Returns a raw file descriptor on success and throws when socket(2) fails. Low-level callers should set non-blocking mode before integrating with the event loop.

const fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
close(fd);

function setNonblocking(fd: number): void

Set a socket to non-blocking mode via fcntl(F_SETFL, O_NONBLOCK).

Throws if either fcntl call fails. High-level Socket.connect() and Socket.listen() call this automatically.

const fd = socket();
setNonblocking(fd);

function setsockopt( fd: number, level: number, optname: number, value: boolean | number | ArrayBuffer ): void

Set socket option. Value can be a boolean/number (written as 4-byte int) or an ArrayBuffer for raw option data.

Throws when setsockopt(2) fails. Boolean and number values are encoded as little-endian 32-bit integers for common POSIX socket options.

setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, true);

function getsockopt(fd: number, level: number, optname: number, bufSize: number = 4): ArrayBuffer

Get socket option. Returns the raw ArrayBuffer (4 bytes for int options).

Throws when getsockopt(2) fails. Interpret integer options with a little-endian DataView.

const buf = getsockopt(fd, SOL_SOCKET, SO_ERROR);
const errno = new DataView(buf).getInt32(0, true);

function joinMulticastGroup(fd: number, options: { group: string; interfaceAddress?: string; interfaceIndex?: number; }): void

Join an IPv4 or IPv6 multicast group on a datagram socket.

IPv4 membership uses interfaceAddress when provided. IPv6 membership uses interfaceIndex, which is required for many link-local multicast cases.

joinMulticastGroup(fd, { group: '224.0.0.251' });

function leaveMulticastGroup(fd: number, options: { group: string; interfaceAddress?: string; interfaceIndex?: number; }): void

Leave an IPv4 or IPv6 multicast group previously joined on a socket.

function setMulticastOptions(fd: number, options: { family?: 'ipv4' | 'ipv6'; ttl?: number; loopback?: boolean; interfaceAddress?: string; interfaceIndex?: number; }): void

Set common multicast send options on a datagram socket.

ttl maps to IPv4 TTL or IPv6 hop limit. loopback controls whether local multicast sends are looped back to local receivers. Interface selection uses IPv4 interfaceAddress or IPv6 interfaceIndex.

function networkInterfaces(): NetworkInterface[]

Return kernel network interfaces that have an interface index.

This wraps if_nameindex(3) and is intentionally small: it returns the metadata needed for scoped IPv6 multicast sends and joins. Use interfaceIndex() when resolving a known interface name.

for (const iface of networkInterfaces()) console.log(iface.index, iface.name);

function interfaceIndex(name: string): number

Return the kernel interface index for an interface name.

const en0 = interfaceIndex('en0');

function networkInterfaceIndices(): number[]

Return network interface indexes, omitting names for allocation-light callers.

const indexes = networkInterfaceIndices();

function getsockname(fd: number): Address | UnknownAddress

Return the local address currently bound to a socket fd.

This is useful after binding port 0 to discover the assigned port. Unknown address families are represented with UnknownAddress.

const addr = getsockname(fd);
console.log(addr);

function bind(fd: number, addr: Address): void

Bind a socket to an address.

Throws on native bind errors. When the address is already in use, the error message includes the port when available.

bind(fd, { family: 'ipv4', ip: '127.0.0.1', port: 3000 });

function listen(fd: number, backlog: number = 128): void

Mark a socket as passive (server socket).

backlog defaults to 128. The socket must already be bound. Throws when listen(2) fails.

listen(fd, 128);

function accept(serverFd: number, setNonblock: boolean = true): { fd: number; addr: Address | UnknownAddress; } | null

Accept a pending connection. Returns { fd, addr }. If the socket is non-blocking and no connection is pending, returns null.

On Linux, the accepted socket is made non-blocking atomically via accept4.

Throws for accept errors other than EAGAIN/EWOULDBLOCK.

const accepted = accept(serverFd);
if (accepted !== null) close(accepted.fd);

function connect(fd: number, addr: Address): number

Initiate a connection to a remote address. For non-blocking sockets, returns -115 (EINPROGRESS) on Linux or -36 (EINPROGRESS) on macOS — use fino:loop addWrite() to wait for completion, then check SO_ERROR via getsockopt().

const rc = connect(fd, { family: 'ipv4', ip: '127.0.0.1', port: 80 });
if (rc === EINPROGRESS) await loop.writable(fd);

function send(fd: number, data: Uint8Array | ArrayBuffer, flags: number = 0): number

Send data on a connected socket.

Returns bytes sent or a negative errno. Short writes are possible and must be handled by low-level callers.

const n = send(fd, new TextEncoder().encode('GET / HTTP/1.1\\r\\n\\r\\n'));

function recv(fd: number, maxBytes: number = 65536, flags: number = 0): Uint8Array | number | null

Receive data from a connected socket.

Returns Uint8Array bytes, null on EOF, or a negative errno on error. For non-blocking fds, EAGAIN is returned as the platform-specific negative errno.

const chunk = recv(fd);
if (chunk === null) console.log('peer closed');

function sendto( fd: number, data: Uint8Array | ArrayBuffer, destAddr: Address, flags: number = 0 ): number

Send a datagram to a specific address (UDP).

Returns bytes sent or a negative errno. The socket does not need to be connected.

sendto(fd, packet, { family: 'ipv4', ip: '8.8.8.8', port: 53 });

function sendmsgEcn( fd: number, data: Uint8Array | ArrayBuffer, destAddr: Address, ecn: number, flags: number = 0 ): number

Send one UDP datagram with ECN traffic-class ancillary data.

The ECN value is masked to its low two bits. IPv4 sends IP_TOS; IPv6 sends IPV6_TCLASS. Returns bytes sent or a negative errno.

sendmsgEcn(fd, packet, { family: 'ipv4', ip: '127.0.0.1', port: 4433 }, 2);

function sendmmsgBatch(fd: number, packets: SendmsgBatchPacket[], flags: number = 0): { sent: number; errno: number | null; } | null

Send a batch of UDP datagrams with the platform batch-send primitive.

Linux uses sendmmsg(2). macOS uses Darwin sendmsg_x. Returns null on platforms without a batch primitive, otherwise returns the number of messages accepted by the kernel or a negative errno when none were sent. Per-message ECN values are carried as ancillary traffic-class data.

function recvfrom(fd: number, maxBytes: number = 65536, flags: number = 0): { data: Uint8Array; addr: Address | UnknownAddress; } | number

Receive a datagram (UDP). Returns { data, addr } or null on EAGAIN.

This implementation returns a negative errno for receive errors, including EAGAIN, rather than null. Successful results include the sender address.

const packet = recvfrom(fd, 4096);
if (typeof packet !== 'number') console.log(packet.addr, packet.data);

function recvmsgEcn(fd: number, maxBytes: number = 65536, flags: number = 0): { data: Uint8Array; addr: Address | UnknownAddress; ecn?: number; } | number

Receive one UDP datagram and parse ECN traffic-class ancillary data.

The socket must have IP_RECVTOS or IPV6_RECVTCLASS enabled first. The returned ecn value is masked to the two ECN bits when present; kernels may omit ancillary data for packets that arrived without a traffic-class mark.

setsockopt(fd, IPPROTO_IP, IP_RECVTOS, true);
const packet = recvmsgEcn(fd, 4096);

function recvmsgPacketInfo(fd: number, maxBytes: number = 65536, flags: number = 0): { data: Uint8Array; addr: Address | UnknownAddress; destination?: Address; interfaceIndex?: number; } | number

Receive one UDP datagram and parse packet-info ancillary data.

Enable IP_RECVPKTINFO for IPv4 or IPV6_RECVPKTINFO for IPv6 before calling this helper. Kernels may omit packet-info metadata for some local paths; in that case destination and interfaceIndex are absent while the datagram payload and source address are still returned.

setsockopt(fd, IPPROTO_IP, IP_RECVPKTINFO, true);
const packet = recvmsgPacketInfo(fd, 4096);

function recvmmsgBatch( fd: number, maxPackets: number, maxBytes: number = 65536, flags: number = 0 ): RecvmsgBatchPacket[] | number | null

Receive multiple UDP datagrams with Linux recvmmsg(2).

Returns null on platforms without recvmmsg, a negative errno when no packet was received, or an array of datagrams with optional ECN metadata.

function shutdown(fd: number, how: number = SHUT_RDWR): void

Shut down part or all of a socket connection.

Errors from shutdown(2) are ignored to keep close paths idempotent. Use SHUT_RD, SHUT_WR, or SHUT_RDWR.

shutdown(fd, SHUT_WR);

function close(fd: number): void

Close a socket.

Errors from close(2) are ignored. After calling this, the fd must not be reused by user code.

close(fd);

async function connectTcp(addr: Address, opts: ConnectOptions = {}): Promise<number>

Establish a non-blocking TCP connection and return the raw fd. Used by both Socket.connect() and TlsSocket.connect() (in fino:tls) to avoid duplicating the connect/SO_ERROR dance.

Throws if socket creation, connection, or SO_ERROR checking fails. On error, the temporary fd is closed before the error is rethrown.

const fd = await connectTcp({ family: 'ipv4', ip: '127.0.0.1', port: 80 }, { noDelay: true });

Classes

class Socket {

A connected socket that can be split into independent Reader and Writer halves. The underlying fd is closed automatically when both halves close.

Use the static factories rather than the constructor directly:

const sock = await Socket.connect({ family: 'ipv4', ip: '127.0.0.1', port: 80 });
const server = Socket.listen({ family: 'ipv6', ip: '::', port: 8080 });

Constructors

constructor(fd: number, remoteAddr: Address | null, localAddr: Address | null)

Wrap an already-open socket fd.

The constructor does not set non-blocking mode and does not duplicate the fd. Prefer Socket.connect() or Socket.listen() unless integrating with a lower-level accept/connect path.

const sock = new Socket(fd, remoteAddr, localAddr);

Getters

get fd()

Raw file descriptor for advanced use with the low-level API.

console.log(socket.fd);
get remoteAddress()

Remote address object passed to Socket.connect(), or the peer address for accepted sockets when known.

console.log(socket.remoteAddress);
get localAddress()

Local bound address when known.

Client sockets created by Socket.connect() currently expose null; accepted sockets expose the listener address.

console.log(socket.localAddress);
get closed()

Whether close() has been called or both split halves have closed.

if (!socket.closed) socket.close();

Methods

split(): [BufferedBytesReader, BufferedBytesWriter]

Split into a [Reader, Writer pair. The underlying fd is closed automatically when both halves have been closed.

The Reader's close sends SHUT_RD (wakes any in-flight read with EOF). The Writer's close sends SHUT_WR (sends FIN to the peer).

const [reader, writer] = socket.split();
await writer.write(new TextEncoder().encode('hello'));
await writer.close();
await reader.close();
close(): void

Immediately close the socket (both directions). Calls shutdown(SHUT_RDWR) then close(fd). Idempotent.

socket.close();
socket.close();

Static Methods

static async connect(addr: Address, opts: ConnectOptions = {}): Promise<Socket>

Open a non-blocking TCP or Unix-domain client connection.

Resolves with a connected Socket. Throws on connection errors; temporary fds are closed before the error is rethrown.

const socket = await Socket.connect({ family: 'ipv4', ip: '127.0.0.1', port: 80 }, { noDelay: true });
static listen(addr: Address, opts: ListenOptions = {}): Server

Create a listening server. Returns a Server object that is an async iterable of incoming Socket connections.

Supports IPv4, IPv6, and Unix domain sockets via addr.family.

If addr.port is zero, the returned server address contains the assigned port. Unix-domain paths are unlinked before binding. Throws if bind/listen fails or if getsockname() returns an unsupported address family.

const server = Socket.listen({ family: 'ipv4', ip: '127.0.0.1', port: 0 });
const conn = await server.accept();
conn?.close();
server.close();