std.net
API summary (generated from the Beans source by npm run coverage): 6 types · 1 constructor · 8 static methods · 39 instance methods · 4 public fields.
std.net provides TCP and UDP sockets and name resolution. It is the readable
layer over the raw socket syscalls in std.sock. The
source is stdlib/std/net/net.b.
import std.netRules that shape the package
Section titled “Rules that shape the package”- Sockets are made with a named static, not a constructor. Construction that
can fail returns a
Result, so you writeTcpStream.connect(...),TcpListener.bind(...), andUdpSocket.bind(...), the same shape asFile.open. - Every socket is move-only and
Send. It closes itself indeinitwhen it goes out of scope. Move it into asend fnworker when one thread should own the connection.close()exists for when you want to see the close error; otherwise scope exit handles it. - The address family is resolved, never chosen. Every entry point runs the
host through
getaddrinfo, so"localhost","127.0.0.1", and"::1"all work with no family flag. - Reads and writes are partial by contract.
readreturns what has arrived andwritereports what went out. An emptyBytesfromreadmeans the peer closed.write_allandread_exactloop for you. - Blocking calls retry on EINTR. When a configured timeout expires, the call
returns an
errwith kindtimeout.
Error kinds you may see: refused, in_use, timeout, reset, unreachable,
not_found, closed, eof, invalid, permission, io.
Address
Section titled “Address”An ordinary value: a numeric host and a port. Copy it freely.
pub class Addressnew Address(host: string, port: int)
pub host: stringpub port: int
pub static fn resolve(host: string, port: int) -> Result<List<Address>>pub fn to_string() -> stringpub fn is_ipv6() -> boolpub fn is_loopback() -> boolresolvereturns every distinct numeric address a name maps to, in resolver order. A name that does not resolve is anerrwith kindnot_found.to_stringprintshost:port, or[host]:portfor IPv6.
Datagram
Section titled “Datagram”One received UDP message: the sender’s address and the bytes.
pub class Datagrampub from: Addresspub data: BytesByteStream
Section titled “ByteStream”The transport shape shared by raw TCP and TLS. HTTP/2 and WebSocket use this interface, so the same protocol code works over either transport.
pub interface ByteStream { fn write_all(data: Bytes) -> Result<int> fn read(max: int) -> Result<Bytes> fn shutdown_write() -> Result<bool> fn close() -> Result<bool> fn poll_handle() -> int}TcpStream
Section titled “TcpStream”A connected TCP socket. Move-only; pass it with move, take it out of a
Result with ?.
pub unique class TcpStream implements ByteStream, Send
pub static fn connect(host: string, port: int) -> Result<TcpStream>pub static fn connect_timeout(host: string, port: int, ms: int) -> Result<TcpStream>
pub fn write(data: Bytes) -> Result<int>pub fn write_all(data: Bytes) -> Result<int>pub fn write_text(text: string) -> Result<int>pub fn write_from(data: Bytes, offset: int) -> Result<int>pub fn try_write_from(data: Bytes, offset: int) -> Result<Option<int>>pub fn write_vectored(head: Bytes, body: Bytes, offset: int) -> Result<int>pub fn write_vectored_text(head: Bytes, body: string, offset: int) -> Result<int>pub fn read(max: int) -> Result<Bytes>pub fn read_into(buffer: Bytes) -> Result<int>pub fn read_into_waiting(buffer: Bytes) -> Result<int>pub fn try_read_into(buffer: Bytes) -> Result<Option<int>>pub fn read_exact(count: int) -> Result<Bytes>pub fn read_to_end(limit: int) -> Result<Bytes>pub fn peer_address() -> Result<Address>pub fn local_address() -> Result<Address>pub fn set_timeouts(read_ms: int, write_ms: int) -> Result<bool>pub fn set_nonblocking(on: bool) -> Result<bool>pub fn set_nodelay(on: bool) -> Result<bool>pub fn into_raw() -> Result<int>pub fn shutdown_write() -> Result<bool>pub fn shutdown_read() -> Result<bool>pub fn close() -> Result<bool>pub fn poll_handle() -> int-
connectwaits as long as the OS does;connect_timeoutgives up aftermsmilliseconds with kindtimeout. -
write_vectoredsends two buffers as one write, without joining them first. For an HTTP response, this avoids copying the body into a buffer with the head.write_vectored_textdoes the same for a body held as a string.offsetcounts into the concatenation of the two buffers, so a short write resumes correctly whether it stopped inside the head or inside the body and you never have to work out which. Pass0first, add what came back, and call again untiloffsetreacheshead.len() + body.len(); at that point it answersok(0). -
writemay send less than all ofdataand returns the count.write_allloops until everything is sent.readreturns up tomaxbytes; an empty result means the peer closed.read_exactfails with kindeofif the peer closes beforecountbytes arrive.read_exactandread_to_endgrow one result buffer instead of joining copied chunks.write_textsends string storage directly. -
read_intowrites into an existing non-emptyBytesand returns the number of bytes written. Zero means EOF. The buffer keeps its length and only0..countbelongs to that read, so oneBytes.filled(...)allocation can serve the whole connection. -
read_into_waitingreads likeread_into, but on a fiber it waits for readability before the first recv. A caller that just drained the socket knows the next recv would only report would-block, so this form spends one poller wait instead of that wasted syscall. Off a fiber it behaves exactly likeread_into. -
write_fromwrites starting atoffsetwithout slicing or copyingdata— the offset-aware form an output queue needs to resume a short write. -
The
try_pair serves nonblocking streams:try_write_fromandtry_read_intoreturnok(none)when the socket would block, instead of an error. Fortry_read_into,ok(some(0))is EOF, so a quiet socket and a closed peer remain different facts. -
set_nodelay(true)disables Nagle’s algorithm (andfalserestores it). Disabling it can reduce delays for small request/response messages. -
into_rawtransfers the descriptor to a lower-level transport. The stream stops owning it; the new owner must close it. -
shutdown_writesends EOF to the peer while keeping the read half open. -
poll_handlereturns the descriptor borrowed, for registering with a poller. It does not transfer ownership; do not close it.
A short request and reply over loopback:
import std.ioimport std.net
fn main() { let listener: net.TcpListener = net.TcpListener.bind("127.0.0.1", 0).expect("bind") let port: int = listener.port().expect("port")
let client: net.TcpStream = net.TcpStream.connect("127.0.0.1", port).expect("connect") let server: net.TcpStream = listener.accept().expect("accept")
client.write_text("ping").expect("write") let got: Bytes = server.read(64).expect("read") io.println(got.to_string())
client.close().expect("close") server.close().expect("close")}TcpListener
Section titled “TcpListener”A socket that accepts incoming TCP connections.
pub unique class TcpListener implements Send
pub static fn bind(host: string, port: int) -> Result<TcpListener>pub static fn bind_with_backlog(host: string, port: int, depth: int) -> Result<TcpListener>pub static fn bind_reuse_port(host: string, port: int) -> Result<TcpListener>pub static fn bind_reuse_port_with_backlog(host: string, port: int, depth: int) -> Result<TcpListener>
pub fn accept() -> Result<TcpStream>pub fn accept_timeout(ms: int) -> Result<TcpStream>pub fn try_accept() -> Result<Option<TcpStream>>pub fn local_address() -> Result<Address>pub fn port() -> Result<int>pub fn set_nonblocking(on: bool) -> Result<bool>pub fn close() -> Result<bool>pub fn poll_handle() -> intbinduses a backlog of 128;bind_with_backlogsets the accept-queue depth.bind_reuse_portlets independent listeners share one port. Whether that spreads load is the kernel’s decision, not the call’s. Linux hashes each connection’s four-tuple across the listening sockets, so N listeners serve N shares. macOS does not balance at all — the last socket to bind receives every connection and the rest stay idle, which is BSD behaviour, and Darwin has no equivalent of FreeBSD’sSO_REUSEPORT_LB. Use it for more cores on Linux, and for handing a port over without dropping connections anywhere; on macOS spread work by accepting on one listener and dealing the streams out to workers. Windows returns kindunsupported. The_with_backlogform also sets the queue depth.- Port
0asks the system for a free port. Read it back withport(), which is how a test binds without guessing a number. acceptblocks until a connection arrives.accept_timeout(0)is a non-blocking check; a positive timeout that runs out is kindtimeout.try_acceptaccepts one connection without waiting:ok(none)means the accept queue is empty. This is the form a poller-driven accept loop uses after the listener’s descriptor reports readable.
UdpSocket
Section titled “UdpSocket”A bound UDP socket. Each send is one message; each receive returns one message with the sender’s address attached.
pub unique class UdpSocket implements Send
pub static fn bind(host: string, port: int) -> Result<UdpSocket>
pub fn send_to(data: Bytes, to: Address) -> Result<int>pub fn recv_from(max: int) -> Result<Datagram>pub fn local_address() -> Result<Address>pub fn port() -> Result<int>pub fn set_timeouts(read_ms: int, write_ms: int) -> Result<bool>pub fn set_nonblocking(on: bool) -> Result<bool>pub fn close() -> Result<bool>pub fn poll_handle() -> intsend_toreports how many bytes went. A datagram is sent whole or not at all, so a short count means the message was too large.recv_fromreads one datagram up tomaxbytes; anything pastmaxin a single datagram is dropped by the OS, so sizemaxto your protocol. The returnedDatagramtakes ownership of the received payload without joining and slicing one combined runtime buffer.
Two UDP sockets on loopback, one sending to the other:
import std.ioimport std.net
fn main() { let inbox: net.UdpSocket = net.UdpSocket.bind("127.0.0.1", 0).expect("bind") let port: int = inbox.port().expect("port")
let sender: net.UdpSocket = net.UdpSocket.bind("127.0.0.1", 0).expect("bind") let target: net.Address = new net.Address("127.0.0.1", port) sender.send_to(Bytes.from("hello"), target).expect("send")
let note: net.Datagram = inbox.recv_from(64).expect("recv") io.println("from {note.from.to_string()}: {note.data.to_string()}")}Multicast
Section titled “Multicast”pub fn join_multicast(group: string) -> Result<bool>pub fn leave_multicast(group: string) -> Result<bool>Joins or leaves a multicast group. Joined sockets receive datagrams sent to the
group. Use a numeric address, such as "239.1.2.3" or "ff02::1"; the socket
must be bound to the same address family. Leaving a group that this socket has
not joined returns an err from the OS.
Readiness
Section titled “Readiness”To wait for a socket to become readable or writable without spinning, register
the descriptor from poll_handle() with the
std.poll poller and wait for its events.