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.serveruns until a worker fails. Each worker is a native thread.
Import as TcpServer with import silk.net.tcp_server { TcpServer }.
Public declarations: 7.
Verdict#
pub enum VerdictWhat happens to a connection after Protocol.receive.
KeepOpen#
KeepOpenKeep the connection open, and call receive again when more bytes arrive.
Close#
CloseSend the output that is buffered, then close the connection.
Received#
pub struct ReceivedThe answer of Protocol.receive: how many input bytes it consumed, and what happens next.
Field consumed#
pub consumed: usizeThe number of bytes at the start of the input that the protocol used. The engine drops them.
Field verdict#
pub verdict: VerdictWhether the connection stays open.
Implementation Copy for Received#
impl Copy for ReceivedProtocol#
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#
effect fn open(self: &Self) -> S ! OutOfMemoryError ? &mut AllocatorMakes the state of a new connection.
Operation receive#
effect fn receive(
self: &Self,
state: &mut S,
input: &mut [u8],
ended: bool,
output: &mut List<u8>,
) -> Received ! OutOfMemoryError ? &mut AllocatorConsumes 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#
pub struct TcpServerOptionsThe settings of TcpServer.serve.
Field address#
pub address: IpAddressThe address to listen on.
Field port#
pub port: u16The TCP port to listen on.
Field workers#
pub workers: usizeThe number of worker threads. Zero means one for each processor.
Field backlog#
pub backlog: usizeThe kernel backlog of each listener.
Field readBufferBytes#
pub readBufferBytes: usizeThe first size of the input buffer of a connection that has a partial request.
Field maxInputBytes#
pub maxInputBytes: usizeThe largest size of the input buffer of a connection.
Field idleTimeoutSeconds#
pub idleTimeoutSeconds: usizeThe time a connection can stay silent before it is closed, in seconds. Zero means no limit.
Implementation Copy for TcpServerOptions#
impl Copy for TcpServerOptionsImplementation TcpServerOptions#
impl TcpServerOptionsAssociated function make#
pub fn make(port: u16) -> TcpServerOptionsReturns 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#
pub enum TcpServerOperationThe system call that failed in TcpServerError.
Socket#
Socketsocket: make the listener.
SetSocketOption#
SetSocketOptionsetsockopt: set a listener option.
Bind#
Bindbind: bind the listener to the address.
Listen#
Listenlisten: start listening.
EventLoop#
EventLoopepoll_create1: make the event loop.
Watch#
Watchepoll_ctl: watch a socket.
Wait#
Waitepoll_wait: wait for events.
Spawn#
Spawnpthread_create: start a worker thread.
Implementation Debug for TcpServerOperation#
impl Debug for TcpServerOperationOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut WriterTcpServerError#
pub struct TcpServerErrorA system call of the engine failed.
Field operation#
pub operation: TcpServerOperationThe system call that failed.
Field code#
pub code: i32The system error code, such as 98 for an address that is in use on Linux.
Implementation Debug for TcpServerError#
impl Debug for TcpServerErrorOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut WriterTcpServer#
pub struct TcpServerThe entry point of the engine.
Implementation TcpServer#
impl TcpServerAssociated function serve#
pub effect fn serve<S, P: Protocol<S>>(options: TcpServerOptions, protocol: P) -> () ! TcpServerError | OutOfMemoryError ? &mut AllocatorServes 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.