silk.net.tcp_server.

A multi-core TCP server engine: one event loop for each worker thread, and a protocol that turns received bytes into bytes to send.

When to use#

Use it to serve a byte protocol, such as HTTP/1.1 in silk.http.serve, over many connections at the same time with little overhead. Write a Protocol, then call TcpServer.serve with TcpServerOptions.

Details#

Each worker thread has its own listener socket on the same address, with SO_REUSEPORT, and its own epoll instance. The kernel spreads new connections over the listeners. A worker accepts, reads, calls the protocol and writes on its own thread, so workers share nothing.

When bytes arrive, the engine calls Protocol.receive with all bytes that were not consumed yet. The protocol consumes a prefix, appends its answer to the output, and says whether the connection stays open. The engine sends the output at once, and waits for the socket when the kernel cannot take it all.

Each worker reads into one shared buffer of 64 KiB (at most maxInputBytes), and the protocol writes into one shared output list. Only bytes that are left over, a partial request or output that the kernel did not take, move into a buffer of the connection, which is freed when it drains. So an idle connection holds no buffer. A connection buffer starts at readBufferBytes. When it is full and the protocol consumed nothing, it doubles, up to maxInputBytes. A protocol that needs more than that closes the connection itself first, with an answer such as 413 Content Too Large.

A connection that sends nothing for idleTimeoutSeconds is closed.

Errors#

TcpServerError tells the system call that failed and the system error code. It ends TcpServer.serve. Errors of one connection only close that connection.

Gotchas#

  • It needs Linux with GNU libc on x86_64 or aarch64. On other targets, a call fails to compile.

  • TcpServer.serve runs until a worker fails. Each worker is a native thread.

Import as TcpServer with import silk.net.tcp_server { TcpServer }.

Public declarations: 7.

Verdict#

silk
pub enum Verdict

What happens to a connection after Protocol.receive.

KeepOpen#

silk
KeepOpen

Keep the connection open, and call receive again when more bytes arrive.

Close#

silk
Close

Send the output that is buffered, then close the connection.

Received#

silk
pub struct Received

The answer of Protocol.receive: how many input bytes it consumed, and what happens next.

Field consumed#

silk
pub consumed: usize

The number of bytes at the start of the input that the protocol used. The engine drops them.

Field verdict#

silk
pub verdict: Verdict

Whether the connection stays open.

Implementation Copy for Received#

silk
impl Copy for Received

Protocol#

silk
pub interface Protocol<S>

A byte protocol that the engine runs on each connection.

Details#

S is the state of one connection. The engine makes it with open when it accepts the connection, and drops it when the connection closes. All calls for one connection happen on one worker thread, one at a time. One protocol value serves all workers through a shared borrow.

Operation open#

silk
effect fn open(self: &Self) -> S ! OutOfMemoryError ? &mut Allocator

Makes the state of a new connection.

Operation receive#

silk
effect fn receive(
    self: &Self,
    state: &mut S,
    input: &mut [u8],
    ended: bool,
    output: &mut List<u8>,
  ) -> Received ! OutOfMemoryError ? &mut Allocator

Consumes a prefix of input, and appends the bytes to send to output.

Details#

input holds every received byte that was not consumed yet, so the protocol can wait for a complete message and consume nothing. ended is true when the peer closed its side, so no more bytes come. The protocol can change input in place, for example to decode a body.

TcpServerOptions#

silk
pub struct TcpServerOptions

The settings of TcpServer.serve.

Field address#

silk
pub address: IpAddress

The address to listen on.

Field port#

silk
pub port: u16

The TCP port to listen on.

Field workers#

silk
pub workers: usize

The number of worker threads. Zero means one for each processor.

Field backlog#

silk
pub backlog: usize

The kernel backlog of each listener.

Field readBufferBytes#

silk
pub readBufferBytes: usize

The first size of the input buffer of a connection that has a partial request.

Field maxInputBytes#

silk
pub maxInputBytes: usize

The largest size of the input buffer of a connection.

Field idleTimeoutSeconds#

silk
pub idleTimeoutSeconds: usize

The time a connection can stay silent before it is closed, in seconds. Zero means no limit.

Implementation Copy for TcpServerOptions#

silk
impl Copy for TcpServerOptions

Implementation TcpServerOptions#

silk
impl TcpServerOptions

Associated function make#

silk
pub fn make(port: u16) -> TcpServerOptions

Returns options for 127.0.0.1 and port, one worker for each processor, an 8 KiB input buffer that grows up to 1 MiB, a backlog of 4096, and a 60 second idle timeout.

TcpServerOperation#

silk
pub enum TcpServerOperation

The system call that failed in TcpServerError.

Socket#

silk
Socket

socket: make the listener.

SetSocketOption#

silk
SetSocketOption

setsockopt: set a listener option.

Bind#

silk
Bind

bind: bind the listener to the address.

Listen#

silk
Listen

listen: start listening.

EventLoop#

silk
EventLoop

epoll_create1: make the event loop.

Watch#

silk
Watch

epoll_ctl: watch a socket.

Wait#

silk
Wait

epoll_wait: wait for events.

Spawn#

silk
Spawn

pthread_create: start a worker thread.

Implementation Debug for TcpServerOperation#

silk
impl Debug for TcpServerOperation

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut Writer

TcpServerError#

silk
pub struct TcpServerError

A system call of the engine failed.

Field operation#

silk
pub operation: TcpServerOperation

The system call that failed.

Field code#

silk
pub code: i32

The system error code, such as 98 for an address that is in use on Linux.

Implementation Debug for TcpServerError#

silk
impl Debug for TcpServerError

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut Writer

TcpServer#

silk
pub struct TcpServer

The entry point of the engine.

Implementation TcpServer#

silk
impl TcpServer

Associated function serve#

silk
pub effect fn serve<S, P: Protocol<S>>(options: TcpServerOptions, protocol: P) -> () ! TcpServerError | OutOfMemoryError ? &mut Allocator

Serves connections with protocol on every worker, until a worker fails.

Gotchas#

It needs Linux with GNU libc on x86_64 or aarch64.

Category: Networking.

Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu-no-libc, x86_64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, wasm32-wasip1, wasm32-wasip2, x86_64-pc-windows-msvc, wasm64-unknown-unknown.

View the source of this page