hanki

socket

stdlib/core/socket.hk: methods on the TCP and Unix-domain stream/listener resource types.

Sockets are runtime-managed native resources (HANKI.md §4), like File: the per-actor heap closes one when its last handle goes, and a dropped socket is never leaked; close! releases it eagerly. A socket handle is single-owner and moves, never copies, across actors. split! invalidates a full stream and produces independently movable read and write handles.

Open TCP with net.connect! / net.listen!, or a pathname Unix-domain socket with net.connect_unix! / net.listen_unix!; the methods here read, write, split, accept, close, and reset a client's deadline (TCP and Unix-domain alike). The primitive operations are @intrinsic in sys; this face delegates, and the [net] effect bubbles up through the call.

There is no hermetic test block here: every method bottoms out in a real OS socket via an @intrinsic, with no pure result to assert in the stdlib test pack. These paths are covered end-to-end by the CLI integration tests and example programs.

TcpStream

TcpStream, or net.TcpStream, is a runtime-managed native resource handle (HANKI.md §4): live native state the per-actor memory manager owns, released when the last handle drops. It is single-owner - never copied, moved across a send - and has no fields of its own, so its methods are its whole surface. A handle is minted by an API that opens one; it is never constructed.

impl TcpStream

read!

def read!(self, max: u32) -> Result<bytes, sys.NetError> [net]

Read once, returning up to max bytes. An empty bytes is EOF (the peer closed). Err(NetError) on an I/O error or a closed socket. @no-doctest: reads a real socket; needs a peer, cannot assert in a doctest

write!

def write!(self, data: bytes) -> Result<i64, sys.NetError> [net]

Write once, returning the count written (which may be less than data.length). For the whole buffer, use net.write_all!. @no-doctest: writes a real socket; needs a peer, cannot assert in a doctest

set_deadline!

def set_deadline!(self, timeout_ms: i32) -> () [net]

Replace this connection's absolute wall-clock deadline. The new budget begins now; negative values mean an immediate poll. An operation that is already ready can still complete without waiting. @no-doctest: mutates a live socket resource

close!

def close!(self) -> () [net]

Release the descriptor now. Idempotent: dropping the last handle also closes a stream, and an explicit close! is therefore an eager optimisation. @no-doctest: side-effecting close; no return value to assert

split!

def split!(self) -> Pair<TcpReadHalf, TcpWriteHalf> [net]

Consume this full handle and return independently movable directional resources. The original handle accepts no later operation. @no-doctest: needs a live TCP connection

TcpReadHalf

TcpReadHalf, or net.TcpReadHalf, is a runtime-managed native resource handle (HANKI.md §4): live native state the per-actor memory manager owns, released when the last handle drops. It is single-owner - never copied, moved across a send - and has no fields of its own, so its methods are its whole surface. A handle is minted by an API that opens one; it is never constructed.

impl TcpReadHalf

read!

def read!(self, max: u32) -> Result<bytes, sys.NetError> [net]

Read once, returning up to max bytes. A locally closed half returns EOF. @no-doctest: reads a real socket; needs a peer

close!

def close!(self) -> () [net]

Close the read direction. The sibling write half remains usable. @no-doctest: side-effecting close; no return value to assert

TcpWriteHalf

TcpWriteHalf, or net.TcpWriteHalf, is a runtime-managed native resource handle (HANKI.md §4): live native state the per-actor memory manager owns, released when the last handle drops. It is single-owner - never copied, moved across a send - and has no fields of its own, so its methods are its whole surface. A handle is minted by an API that opens one; it is never constructed.

impl TcpWriteHalf

write!

def write!(self, data: bytes) -> Result<i64, sys.NetError> [net]

Write once, returning the count accepted. @no-doctest: writes a real socket; needs a peer

close!

def close!(self) -> () [net]

Close the write direction. The peer observes EOF, and the sibling read half remains usable. @no-doctest: side-effecting close; no return value to assert

impl ReadStream<TcpStream>

read!

def read!(self, max: u32) -> Result<bytes, sys.NetError> [net]

See stream.ReadStream.read!. @no-doctest: reads a real socket; needs a peer

impl WriteStream<TcpStream>

write!

def write!(self, data: bytes) -> Result<i64, sys.NetError> [net]

See stream.WriteStream.write!. @no-doctest: writes a real socket; needs a peer

impl ReadStream<TcpReadHalf>

read!

def read!(self, max: u32) -> Result<bytes, sys.NetError> [net]

See stream.ReadStream.read!. @no-doctest: reads a real socket; needs a peer

impl WriteStream<TcpWriteHalf>

write!

def write!(self, data: bytes) -> Result<i64, sys.NetError> [net]

See stream.WriteStream.write!. @no-doctest: writes a real socket; needs a peer

UnixStream

UnixStream, or net.UnixStream, is a runtime-managed native resource handle (HANKI.md §4): live native state the per-actor memory manager owns, released when the last handle drops. It is single-owner - never copied, moved across a send - and has no fields of its own, so its methods are its whole surface. A handle is minted by an API that opens one; it is never constructed.

impl UnixStream

read!

def read!(self, max: u32) -> Result<bytes, sys.NetError> [net]

Read once, returning up to max bytes. An empty bytes is EOF. @no-doctest: reads a real local socket; needs a peer

write!

def write!(self, data: bytes) -> Result<i64, sys.NetError> [net]

Write once, returning the count accepted. Use net.write_all! for the whole buffer. @no-doctest: writes a real local socket; needs a peer

set_deadline!

def set_deadline!(self, timeout_ms: i32) -> () [net]

Replace this connection's absolute wall-clock deadline. The new budget begins now; negative values mean an immediate poll. An operation that is already ready can still complete without waiting. @no-doctest: mutates a live socket resource

close!

def close!(self) -> () [net]

Release the descriptor now. Idempotent. @no-doctest: side-effecting close; no return value to assert

split!

def split!(self) -> Pair<UnixReadHalf, UnixWriteHalf> [net]

Consume this full handle and return independently movable directional resources. The original handle accepts no later operation. @no-doctest: needs a live Unix-domain connection

UnixReadHalf

UnixReadHalf, or net.UnixReadHalf, is a runtime-managed native resource handle (HANKI.md §4): live native state the per-actor memory manager owns, released when the last handle drops. It is single-owner - never copied, moved across a send - and has no fields of its own, so its methods are its whole surface. A handle is minted by an API that opens one; it is never constructed.

impl UnixReadHalf

read!

def read!(self, max: u32) -> Result<bytes, sys.NetError> [net]

Read once, returning up to max bytes. A locally closed half returns EOF. @no-doctest: reads a real local socket; needs a peer

close!

def close!(self) -> () [net]

Close the read direction. The sibling write half remains usable. @no-doctest: side-effecting close; no return value to assert

UnixWriteHalf

UnixWriteHalf, or net.UnixWriteHalf, is a runtime-managed native resource handle (HANKI.md §4): live native state the per-actor memory manager owns, released when the last handle drops. It is single-owner - never copied, moved across a send - and has no fields of its own, so its methods are its whole surface. A handle is minted by an API that opens one; it is never constructed.

impl UnixWriteHalf

write!

def write!(self, data: bytes) -> Result<i64, sys.NetError> [net]

Write once, returning the count accepted. @no-doctest: writes a real local socket; needs a peer

close!

def close!(self) -> () [net]

Close the write direction. The peer observes EOF, and the sibling read half remains usable. @no-doctest: side-effecting close; no return value to assert

impl ReadStream<UnixStream>

read!

def read!(self, max: u32) -> Result<bytes, sys.NetError> [net]

See stream.ReadStream.read!. @no-doctest: reads a real local socket; needs a peer

impl WriteStream<UnixStream>

write!

def write!(self, data: bytes) -> Result<i64, sys.NetError> [net]

See stream.WriteStream.write!. @no-doctest: writes a real local socket; needs a peer

impl ReadStream<UnixReadHalf>

read!

def read!(self, max: u32) -> Result<bytes, sys.NetError> [net]

See stream.ReadStream.read!. @no-doctest: reads a real local socket; needs a peer

impl WriteStream<UnixWriteHalf>

write!

def write!(self, data: bytes) -> Result<i64, sys.NetError> [net]

See stream.WriteStream.write!. @no-doctest: writes a real local socket; needs a peer

impl Stream<UnixStream>

Error

type Error = sys.NetError

read!

def read!(self, max: u32) -> Result<bytes, sys.NetError> [net]

See stream.Stream.read!. @no-doctest: reads a real local socket; needs a peer

write!

def write!(self, data: bytes) -> Result<i64, sys.NetError> [net]

See stream.Stream.write!. @no-doctest: writes a real local socket; needs a peer

close!

def close!(self) -> () [net]

See stream.Stream.close!. @no-doctest: side-effecting close; no return value to assert

UnixListener

UnixListener, or net.UnixListener, is a runtime-managed native resource handle (HANKI.md §4): live native state the per-actor memory manager owns, released when the last handle drops. It is single-owner - never copied, moved across a send - and has no fields of its own, so its methods are its whole surface. A handle is minted by an API that opens one; it is never constructed.

impl UnixListener

accept!

def accept!(self) -> Result<UnixStream, sys.NetError> [net]

Block for the next local connection. Peer addresses are not exposed. @no-doctest: blocks on a real local accept; needs a peer

close!

def close!(self) -> () [net]

Release the descriptor, leaving the pathname for explicit cleanup with fs.delete!. Idempotent. Unix listeners intentionally have no local_address! method. @no-doctest: side-effecting close; no return value to assert

impl Stream<TcpStream>

The Stream face: framing code written against S: Stream works over a socket unchanged.

Each body forwards to the inherent method of the same name above, which is not the self-call it looks like: inherent-wins coherence (HANKI.md §10) resolves self.read! to the inherent impl, never back into this one. Forwarding in place of repeating the sys calls is what binds the two faces together: a guard added to the inherent method is then also in force for every caller that arrives through the trait.

Error

type Error = sys.NetError

read!

def read!(self, max: u32) -> Result<bytes, sys.NetError> [net]

See stream.Stream.read!. @no-doctest: reads a real socket; needs a peer, cannot assert in a doctest

write!

def write!(self, data: bytes) -> Result<i64, sys.NetError> [net]

See stream.Stream.write!. @no-doctest: writes a real socket; needs a peer, cannot assert in a doctest

close!

def close!(self) -> () [net]

See stream.Stream.close!. @no-doctest: side-effecting close; no return value to assert

impl DeadlineStream<TcpStream>

set_deadline!

def set_deadline!(self, timeout_ms: i32) -> () [net]

See stream.DeadlineStream.set_deadline!. @no-doctest: mutates a live socket resource

impl DeadlineStream<UnixStream>

set_deadline!

def set_deadline!(self, timeout_ms: i32) -> () [net]

See stream.DeadlineStream.set_deadline!. @no-doctest: mutates a live socket resource

TcpListener

TcpListener, or net.TcpListener, is a runtime-managed native resource handle (HANKI.md §4): live native state the per-actor memory manager owns, released when the last handle drops. It is single-owner - never copied, moved across a send - and has no fields of its own, so its methods are its whole surface. A handle is minted by an API that opens one; it is never constructed.

impl TcpListener

accept!

def accept!(self) -> Result<TcpStream, sys.NetError> [net]

Block until the next inbound connection arrives and return its TcpStream. Err(NetError) on an I/O error. @no-doctest: blocks on a real accept; needs a peer, cannot assert in a doctest

local_address!

def local_address!(self) -> Result<sys.LocalAddress, sys.NetError> [net]

The address this listener is bound to. An ephemeral bind (net.listen!(host, 0)) reports the port the OS chose, which nothing else recovers. @no-doctest: needs a bound port; environment-dependent, cannot assert in a doctest

close!

def close!(self) -> () [net]

Release the bound port now. Idempotent, like TcpStream.close!. @no-doctest: side-effecting close; no return value to assert