Skip to main content

TcpOverUdpServer

TcpOverUdpServer is the server-side peer of TcpOverUdpClient. It receives packet-preserving KCP datagrams from the previous node, reconstructs the original byte stream, and forwards that stream to the next node. Replies from the next node are encoded back into KCP datagrams and sent downstream toward the previous packet side.

It is normally paired with TcpOverUdpClient on the remote side.

Typical placement​

Server side:

UdpListener -> TcpOverUdpServer -> TcpConnector
UdpListener -> TcpOverUdpServer -> MuxServer -> TcpConnector

Matching client side:

TcpListener -> TcpOverUdpClient -> UdpConnector
TcpListener -> MuxClient -> TcpOverUdpClient -> UdpConnector

The previous side of TcpOverUdpServer must preserve datagram boundaries. The next side is stream-facing.

Basic example​

{
"name": "tcp-over-udp-server",
"type": "TcpOverUdpServer",
"settings": {
"fec": false,
"kcp-send-window": 8192,
"kcp-recv-window": 8192,
"ping-interval-ms": 10000,
"no-recv-timeout-ms": 30000
},
"next": "service-out"
}

Matching service connector:

{
"name": "service-out",
"type": "TcpConnector",
"settings": {
"address": "127.0.0.1",
"port": 8080
}
}

FEC example​

{
"name": "tcp-over-udp-server",
"type": "TcpOverUdpServer",
"settings": {
"fec": true,
"fec-data-shards": 10,
"fec-parity-shards": 3
},
"next": "service-out"
}

FEC settings must match the TcpOverUdpClient side exactly.

Required fields​

settings is optional. If it is absent or not an object, defaults are used.

next is required in normal use. It should point to the stream-side service path that receives reconstructed payload.

Optional settings​

TcpOverUdpServer uses the same setting names and defaults as TcpOverUdpClient.

FieldDefaultValidationDescription
fecfalsebooleanEnables Reed-Solomon forward error correction around KCP datagrams.
fec-data-shards10positive, with total shards less than or equal to 255Number of data shards per FEC block. Used only when fec is enabled.
fec-parity-shards3positive, with total shards less than or equal to 255Number of parity shards per FEC block. Used only when fec is enabled.
kcp-nodelaytruebooleanEnables KCP nodelay mode.
kcp-interval-ms1010..5000KCP update interval.
kcp-resend2>= 0KCP fast-resend threshold.
kcp-no-congestion-controltruebooleanDisables KCP congestion control.
kcp-send-window8192>= 1KCP send window in segments.
kcp-recv-window8192>= 1KCP receive window in segments.
kcp-initial-cwndhalf of kcp-send-window1..kcp-send-windowInitial KCP congestion window.
kcp-rx-minrto-ms30>= 1Minimum KCP retransmission timeout.
kcp-send-buffer-limit0>= 0Backpressure threshold. 0 derives the limit from local send window + remote window + 10, capped at 2147483647.
ping-interval-ms10000>= 1Idle receive interval before sending an internal ping frame.
no-recv-timeout-ms30000greater than ping-interval-msIdle receive interval before closing the line.

KCP transport model​

On upstream Init, the server initializes per-line state, creates a KCP session, starts the KCP timer loop, creates FEC state if enabled, queues an initial internal ping frame, and forwards upstream Init to the next stream-side node.

When packet payload arrives from the previous node:

  1. FEC decoding is applied if enabled.
  2. KCP receives the datagram.
  3. Completed KCP payloads are drained.
  4. The one-byte internal frame flag is removed.
  5. Data frames are forwarded upstream to the next node as reconstructed stream payload.

When stream payload comes back from the next node:

  1. The payload is split into chunks no larger than the effective KCP write MTU.
  2. Each chunk is prefixed with the one-byte data flag.
  3. The framed chunk is sent through KCP.
  4. KCP output datagrams are forwarded downstream to the previous packet side.

Internal frame flags​

Inside the KCP payload, TcpOverUdpServer uses the same one-byte frame flags as the client:

FlagMeaning
0x00Data frame. Bytes after the flag are real stream payload.
0xF0Ping frame. Used as an internal keepalive marker.
0xFFClose frame. Requests shutdown of the paired line.

Empty data frames are discarded.

MTU and padding​

Optional integer mtu inherits core misc.mtu at construction (the core fallback is 1500). The supported range is 78..65535 without FEC, or 86..65535 with FEC. It is an IPv4-based packet budget in bytes. Invalid explicit or inherited values reject construction; an explicit supported override can replace an unsupported core default. Null, wrong types, fractions, zero, negatives and out-of-range numbers are errors. Each instance keeps its validated MTU unchanged.

With F = 0 without FEC or F = 8 with FEC:

KCP MTU = min(mtu - 20 - 8 - F, 64000)
application chunk = KCP MTU - 24 - 1

The 20-byte IPv4 and 8-byte UDP allowances are fixed. The 64,000-byte decimal cap covers the complete KCP packet, including its 24-byte header. The one-byte frame flag occupies KCP payload space; required_padding_left = 1 provides room for it. The effective KCP MTU governs data and aggregated acknowledgement/control output. FEC adds at most 8 bytes to that output, including parity packets.

At mtu: 1500, KCP MTU/application chunk are 1472/1447 without FEC and 1464/1439 with FEC. Budgets of 64028 without FEC or 64036 with FEC reach the cap; larger valid settings, including 65535, retain KCP MTU 64000 and application chunk 63975. The configured budget itself is not capped to 64000.

This is not path-MTU discovery or negotiation. An ordinary IPv6 header needs 20 more bytes than the fixed IPv4 allowance, so a 1500 budget can permit a 1520-byte IPv6 packet. IPv4 options, IPv6 extension headers and later wrappers need extra operator headroom. No family-dependent adjustment occurs.

FEC behavior​

With fec: true, outbound KCP datagrams are wrapped in Reed-Solomon FEC packets. Data shards are emitted immediately, and parity shards are emitted after each configured data-shard block.

On receive, the decoder:

  • accepts FEC-wrapped KCP datagrams
  • feeds valid data shards into KCP
  • tries to recover missing KCP datagrams from parity shards
  • drops invalid FEC packets conservatively

FEC is symmetric. Enable it and configure the same shard counts on both peers.

Backpressure​

Incoming Pause/Resume are advisory and forwarded, including duplicates. They do not gate KCP updates, retransmissions, pings or ready output.

After admitting the complete input and draining generated packet output, the server checks queued and unacknowledged KCP segments. Above kcp-send-buffer-limit it sends its own upstream Pause toward the next stream side; below the threshold it releases that local hold with Resume. Equality preserves the current hold. An ACK-only update can release it without waiting for another output packet. The hold is recorded before notification, and Finish prevents signals toward the closed side. No separate line task or Resume queue is needed.

This setting is an advisory pressure threshold, not a hard memory or admission cap. KCP/FEC processing and session timers remain active throughout the line, so the node cannot permanently bypass itself.

Close and timeout behavior​

If the next stream-facing side finishes, TcpOverUdpServer:

  1. marks upstream as closed so re-entrant callbacks are not reflected back toward the finished side
  2. sends an internal close frame through KCP
  3. flushes KCP output
  4. destroys local line state
  5. forwards downstream Finish to the packet side

If a close frame is received from the remote peer, the server destroys local state, finishes the stream side, and then finishes the packet side if the line is still alive.

If no receive activity happens for ping-interval-ms, a ping frame is sent. If receive inactivity exceeds no-recv-timeout-ms, the line is closed.

Common mistakes​

  • Do not feed TcpOverUdpServer from a stream-only previous node. The previous side must preserve packet boundaries.
  • Do not forget the matching TcpOverUdpClient.
  • Do not enable FEC on only one side.
  • Do not use mismatched FEC shard counts.
  • Do not expect TcpOverUdpServer to be a chain end; it forwards reconstructed stream payload to next.

Node Metadata​

Source-backed metadata:

This node explicitly disables splice for the entire chain through kNodeFlagBlocksSplice, even if kNodeFlagSupportsSplice is also set.

PropertyValue
node flagskNodeFlagBlocksSplice
can_have_prevtrue
can_have_nexttrue
layer_groupkNodeLayer4
layer_group_prev_nodekNodeLayer4
layer_group_next_nodekNodeLayer4
required_padding_left1 byte