std/network/tcp

std/network/src/tcp.trb

TCP: TcpListener accepts connections, TcpStream is one, and resolve turns a name into addresses. Everything that waits for the network answers a Task, and a connection's two directions are a Source and a Sink of Bytes (docs/design/NETWORK.md sections 2 and 4).

A socket is a handle of the runtime, an Int: the types here are TorbScript over the natives of runtime/io.c, and a handle is closed exactly once, by the close() the release of its owner runs.

fn resolve

fn resolve(host: String): Task<Result<List<IpAddress>, NetworkError>>

The addresses host names: a literal address as it is, localhost as the two loopback addresses, and every other name through the system's resolver, in the order it answered.

const addresses = resolve("localhost").await()?

Errors

  • NetworkError that isHostNotFound() where the name resolves to nothing.

type TcpListener

shared type TcpListener with Close

A socket that listens for connections. It has an identity - the operating system's socket - so it is a shared type, and it closes when the last reference to it goes.

using listener = TcpListener.listen(SocketAddress(IpAddress.loopback, 0))?
var connection = listener.accept().await()?

Related

fn listen

static fn listen(address: SocketAddress, backlog: Int = 128): Result<Self, NetworkError>

Listens on address. Port 0 asks the system for a free port, which TcpListener.localAddress then says. An IPv6 address listens for IPv6 only, on every system.

Errors

  • NetworkError that isAddressInUse() where another socket has the address.

fn localAddress

fn localAddress(): SocketAddress

Where it listens, with the port the system chose where port 0 was asked for.

fn accept

fn accept(): Task<Result<TcpStream, NetworkError>>

The next connection. Waits until one arrives; a cancelled accept leaves the connection to the next one.

fn acceptor

fn acceptor(): TcpAcceptor

A value that accepts connections on this listener from any task, on any worker: several accept loops on one listening socket, which is how a server serves on every core (docs/design/NETWORK.md section 4). It holds the socket's handle and nothing else, so it moves to another worker with the task that holds it; it does not own the socket, which the listener closes - and then every accept of every acceptor fails with a closed socket.

fn stop

fn stop()

Stops listening before the listener is released: an accept that waits fails with a closed socket, and so does every later one. Stopping twice is nothing. A server that shuts down gracefully calls it; the release of the listener does the same by itself.

fn close

var fn close()

Stops listening, as TcpListener.stop does: what the release of the listener runs.

type TcpAcceptor

type TcpAcceptor

What TcpListener.acceptor answers: the right to accept on a listening socket, as a value that moves between workers. Several acceptors of one listener accept at once, each connection going to exactly one of them. It is the handle and nothing else - no address, no text - because a task moves to an idle worker only where its frame holds plain values of at most 32 bytes each (docs/design/CONCURRENCY.md section 16, "What crosses a worker").

fn accept

fn accept(): Task<Result<TcpStream, NetworkError>>

The next connection, on the worker of the task that asked. Waits until one arrives.

Errors

  • NetworkError that isClosed() once the listener stopped listening or was released.

type TcpStream

shared type TcpStream with Close

One TCP connection: bytes in both directions, in order, until either side ends its half. A shared type, because it is the operating system's socket; it closes when the last reference to it - the stream, its source or its sink - goes.

using stream = TcpStream.connectTo("localhost", 8080).await()?
var writing = stream.sink()
writing.addText("ping").await()?

Related

fn of

static fn of(handle: Int): Self

A stream over a handle the runtime answered.

fn connect

static fn connect(address: SocketAddress): Task<Result<Self, NetworkError>>

A connection to address.

Errors

  • NetworkError that isConnectionRefused() where nothing listens there.

fn connectTo

static fn connectTo(host: String, port: Int): Task<Result<Self, NetworkError>>

A connection to port of host: the host is resolved, and each of its addresses is tried in turn until one connects. The failure of the last one is the failure.

fn localAddress

fn localAddress(): Result<SocketAddress, NetworkError>

The address of this end.

fn remoteAddress

fn remoteAddress(): Result<SocketAddress, NetworkError>

The address of the other end.

fn receive

fn receive(maximum: Int = 65536): Task<Result<Bytes?, NetworkError>>

At most maximum bytes, as soon as any arrived: Some(bytes), or None once the peer ended its half. One receive at a time; bytes that arrive while nobody receives wait in the operating system. Some always holds at least one byte: a receive whose stream was closed under it fails with NetworkError.closed rather than answer an empty chunk, which would be neither bytes nor the end of the stream.

fn receiveBuffer

fn receiveBuffer(maximum: Int = 65536): Task<Result<Buffer<UInt8>?, NetworkError>>

TcpStream.receive as the ArrayList the bytes arrive in, which a reader that buffers appends without a copy.

fn send

fn send(bytes: Bytes): Task<Result<Void, NetworkError>>

Writes all of bytes; finishes once the operating system took them, which is when a full peer has read.

fn sendBuffer

fn sendBuffer(bytes: Buffer<UInt8>): Task<Result<Void, NetworkError>>

TcpStream.send for bytes that are an ArrayList already, which is copied once instead of twice.

fn shutdown

fn shutdown(): Result<Void, NetworkError>

Ends this half: the peer reads the end of the stream, and may still answer.

fn setNoDelay

fn setNoDelay(enabled: Bool): Result<Void, NetworkError>

Switches TCP_NODELAY: on, the default, sends what is written at once; off lets the system gather small writes (Nagle's algorithm), which helps a program that writes a byte at a time and delays every request and response.

Errors

fn noDelay

fn noDelay(): Result<Bool, NetworkError>

Whether TCP_NODELAY is on: it is, until TcpStream.setNoDelay says otherwise.

Errors

fn setKeepAlive

fn setKeepAlive(idle: Duration?, interval: Duration? = None, count: Int? = None): Result<Void, NetworkError>

Turns TCP keepalive on or off, so that a dead peer is noticed: None for idle turns it off, Some(idle) turns it on and sets how long the connection is idle before the first probe. interval is the time between probes and count how many go unanswered before the connection is dropped; where they are None the system's own values stay. Times are whole seconds, at least 1: a shorter Duration is rounded up to 1 s, and a Duration of zero or less, or a count below 1, is refused before anything is changed. Keepalive is off until this turns it on. Only the arguments are checked up front: the times and the flag are then set one at a time, so a system that fails midway (one that lacks a keepalive time) can leave the earlier ones changed.

stream.setKeepAlive(Some(60.seconds()), interval: Some(10.seconds()), count: Some(5))?

Errors

  • NetworkError that isClosed() where the stream is closed.
  • NetworkError for a Duration of zero or less, or a count below 1, and where the system lacks the option (Windows before 10 1709 has no keepalive times).

fn keepAlive

fn keepAlive(): Result<Duration?, NetworkError>

The idle time before the first keepalive probe, in whole seconds, or None while keepalive is off - which it is until TcpStream.setKeepAlive turns it on.

Errors

fn source

fn source(chunk: Int = 65536): TcpSource

The reading direction as a Source: at most chunk bytes per next(), and None once the peer ended.

fn sink

fn sink(): TcpSink

The writing direction as a Sink: end() ends this half, close() does nothing of its own.

fn close

var fn close()

Closes the socket. What waits on it fails; the peer reads the end of the stream, or a reset.

type TcpSource

shared type TcpSource with Source<Bytes, NetworkError>

The reading direction of a TcpStream: a Source<Bytes, NetworkError>.

fn of

static fn of(stream: TcpStream, chunk: Int): Self

The source of stream: what TcpStream.source answers.

fn next

var fn next(): Task<Result<Bytes?, NetworkError>>

The next chunk of at most the chunk size, as TcpStream.receive answers it: None once the peer is done.

fn close

var fn close()

Nothing of its own: the stream closes once its last holder is gone.

type TcpSink

shared type TcpSink with Sink<Bytes, NetworkError>

The writing direction of a TcpStream: a Sink<Bytes, NetworkError>.

fn of

static fn of(stream: TcpStream): Self

The sink of stream: what TcpStream.sink answers.

fn add

var fn add(item: Bytes): Task<Result<Void, NetworkError>>

Writes all of item, as TcpStream.send does: finished once the operating system took the bytes.

fn end

var fn end(): Task<Result<Void, NetworkError>>

Ends this half of the connection: the peer reads the end of the stream.

fn close

var fn close()

Nothing of its own: the stream closes once its last holder is gone.