Skip to content

Configuration Reference

irrld edited this page Jul 30, 2026 · 6 revisions

Options are scoped the way Netty's are: options configure the thing you created, child_options configure each session a listener accepts. A client has no children, so its options are the session's.

void ConfigureServer() {
  // no ConnectionType, so this is ZDT, the default
  ServerConfig config{"0.0.0.0", 25000, std::chrono::seconds(10)};

  // options: the listener itself
  config.options.max_connections = 4096;

  // child_options: every session the listener accepts
  config.child_options.common.idle_timeout = std::chrono::seconds(30);
  config.child_options.common.encryption = true;
  config.child_options.common.compression = CompressionType::Zstandard;
  config.child_options.common.compression_threshold = 128;
  config.child_options.common.send_queue_capacity = 1024;
  config.child_options.tcp.no_delay = true;
  config.child_options.zdt.max_datagrams_in_flight = 256;
  config.child_options.zdt.rto_min = std::chrono::milliseconds(50);
  config.child_options.zdt.mtu_ladder.Set({1492, 1200, 576});
}

void ConfigureClient() {
  // a client has no children, so its options are the session's
  ClientConfig config{"127.0.0.1", 25000, std::chrono::seconds(10)};
  config.options.common.idle_timeout = std::chrono::seconds(30);
  config.options.zdt.keepalive_interval = std::chrono::milliseconds(500);
}

The tcp and zdt groups are always present and always settable. Only the one matching the session's connection_type is read, and setting the other is neither an error nor a warning, so a zdt block on a TCP session is dead configuration that looks live.

They are plain structs, not a typed-key map: every option is known at compile time, so unset fields simply keep their defaults.

Config

Field Notes
server_ip / server_port Where to connect (client) or bind (server)
connection_timeout Give up on a connection attempt after this. Zero waits forever
connection_type ConnectionType::ZDT (the default) or ConnectionType::TCP
options Listener scope on a server; session scope on a client
child_options Server only: applied to each accepted session

CommonOptions

Applies to any session, whatever the transport.

Option Default Notes
idle_timeout 10 s Drop a session silent this long. Zero disables. TCP leaves this to the OS unless its transport implements one
collect_metrics true Ignored unless built with ZNET_ENABLE_METRICS
encryption true Read only on the accepting side. See Encryption and Compression
compression Default Resolved at session start to whatever the build supports
compression_threshold 128 B Below this, messages go uncompressed
send_queue_capacity 512 Packets a session holds for its worker to encode

send_queue_capacity

This is the backpressure knob. SendPacket returns false once the queue is full, which is how an application learns it is outrunning the link. A refusal loses nothing, since you still hold the packet.

The queue is a ring allocated whole at construction, roughly 32 bytes a slot rounded up to a power of two, with no per-message allocation afterwards. Size it to the largest burst worth absorbing between two of the worker's ticks, not to the total you ever expect to send. A transport queues further messages behind its own congestion window, so this is not the whole picture.

TCPOptions

Read only when connection_type is ConnectionType::TCP. Ignored on a ZDT session.

Option Default Notes
no_delay true Disables Nagle. Leave it on unless you are bulk-transferring
reuse_address true SO_REUSEADDR, so a restart can rebind a port in TIME_WAIT
send_buffer_size 0 Socket send buffer. Zero keeps the OS default
receive_buffer_size 0 Socket receive buffer. Zero keeps the OS default

ZDTOptions

Read only when connection_type is ConnectionType::ZDT, which is the default. Ignored on a TCP session.

Timing and retries

Option Default Notes
rto_min 100 ms Floor on the retransmit timeout, however low the measured RTT
rto_max 2000 ms Ceiling, including backoff
max_retries 10 Retransmits of one message before the connection is closed
keepalive_interval 1000 ms How often to ping an otherwise idle connection
idle_timeout 10 s Close a connection that has heard nothing this long

Lowering rto_min recovers from loss faster on a LAN. On the open internet it causes retransmits of packets that were merely late, which costs bandwidth and can push the congestion controller down.

Windows

Option Default Notes
max_datagrams_in_flight 512 Ceiling on the congestion window, in datagrams
cwnd 4096 Reliable messages allowed in flight. A memory bound, not congestion control

max_datagrams_in_flight is a bound, not the window. ZDT slow-starts from 10 datagrams and backs off on queueing delay rather than on loss; this caps how far it may grow. It counts datagrams, not bytes, so a half-full datagram costs as much as a full one. Bytes in flight are roughly this times the MTU per round trip, which is what governs throughput on a long link.

The two are separate because coalescing puts many messages in one datagram. Holding cwnd near max_datagrams_in_flight would throttle small messages far below what the window actually allows.

Handshake and MTU

Option Default Notes
mtu_ladder 1492, 1200, 576 Candidates, probed largest first. Set with .Set({...}), max 4
handshake_retransmit 250 ms Wait for a reply before resending
handshake_retries_per_rung 4 Attempts at one MTU before stepping down

The settled MTU determines the largest message the connection can carry. See Choosing a Transport.

Limits and abuse protection

Option Default Notes
cookie_secret_rotation 120 s How often the server rotates its return-routability secret
max_half_open 1024 Concurrent half-open handshakes a server tracks
max_connections 4096 Established connections a server holds
per_source_handshake_rate 20/s Handshake messages accepted per source address
reassembly_timeout 5 s Discard a partial message after this
max_reassembly_bytes 16 MiB Ceiling on bytes held in partial reassemblies
max_reassemblies 256 Concurrent partially reassembled messages
max_inbox_datagrams 4096 Raw datagrams queued per connection before arrivals are dropped
outbound_queue_capacity 4096 Encoded messages the transport holds before Send() fails

Reaching max_reassembly_bytes refuses to start new messages rather than discarding data already accepted, so a peer stalls instead of losing anything.

outbound_queue_capacity sits past the point where a caller can be told to try again: the message is already encoded, so a refusal here drops it rather than pushing back the way a full send_queue_capacity does. That is why its default is generous rather than tuned. The transport also keeps a staging queue, where a shut congestion window parks messages, and refills from the ring only while it holds fewer than this many, so it can hold up to twice this figure in total.

Socket buffers

Option Default Notes
socket_recv_buffer 4 MiB SO_RCVBUF on the UDP socket. Zero leaves the OS default
socket_send_buffer 4 MiB SO_SNDBUF, on the same terms

One socket serves every connection on an endpoint, so these are per endpoint, not per session. They only have to absorb bursts that arrive between drains by the receive thread; max_inbox_datagrams is the designed backpressure point, not these.

Both are best-effort. The kernel clamps silently to its own ceiling (net.core.rmem_max and net.core.wmem_max on Linux, often well below 4 MiB out of the box), so asking for more than it allows is not an error and warns about nothing. znet reads the granted size back and logs both at debug level:

ZDT socket buffers: asked 4194304/4194304, granted 425984/425984 (recv/send bytes, ...)

Raise the sysctls if the larger ask has to take effect. Going much beyond what covers a scheduler stall buys little: the receive thread's drain rate is the bottleneck, and a very deep kernel queue makes every session wait behind a flood rather than letting the per-session inbox caps drop it fairly.

ServerOptions

Listener scope: things that exist before any session does.

Option Default Notes
backlog 0 Pending-connection backlog. Zero uses SOMAXCONN. TCP only
max_connections 0 Cap on accepted connections. Zero means unlimited
reuse_address true SO_REUSEADDR on the listening socket

Note that ZDTOptions::max_connections and ServerOptions::max_connections are different settings: the ZDT one is enforced by the transport before a session exists, the listener one by the server.

Clone this wiki locally