Skip to content

Protocol Reference EN

b23r0 edited this page Oct 12, 2026 · 4 revisions

English · 简体中文 · Home

RakNet transport reference

This guide follows a packet from discovery through connection setup, delivery and loss recovery. It describes the RakNet UDP transport implemented by rust-raknet, including its binary layouts and implementation limits. It is not a specification of every plugin, security extension or protocol revision in the original RakNet SDK.

Current documentation target: 1.4.0, published on crates.io, source b909c93. The wire-format description refers to this implementation; it is not a complete specification of every RakNet dialect.

Contents

  1. Layers and terminology
  2. Primitive encodings
  3. Discovery and offline handshake
  4. Connected handshake and control messages
  5. Datagrams and encapsulated frames
  6. Five delivery modes and indexes
  7. ACK/NACK and loss recovery
  8. Fragmentation and MTU
  9. Batching and local scheduling
  10. Lifecycle, limits and security
  11. Interoperability checklist and source map

Layers and terminology

Application message (payload begins with 0xfe)
    ↓ delivery mode, reliable/sequence/order indexes, optional split metadata
Encapsulated frame [one or more per frame-set datagram]
    ↓ datagram ID + 24-bit datagram sequence
UDP payload
    ↓ UDP + IP headers
Network

A message is an application payload with preserved boundaries. A frame carries that message or one fragment, plus delivery metadata. A frame set is a UDP datagram containing one or more frames. These names are not interchangeable: one datagram ACK can acknowledge several reliable frames.

Discovery, offline connection requests, ACK and NACK packets travel directly in UDP. The connected handshake and application messages normally travel inside frame sets. Transport reliability means receipt at the peer transport; it does not prove that the peer application consumed, persisted or accepted a command.

Primitive encodings

Primitive Encoding used here
u8 One byte
u16, u32, u64, i64 Big endian unless explicitly marked otherwise
u24 sequence/index Three bytes, little endian, modulo 2²⁴
String Big-endian u16 UTF-8 byte length, then bytes
Offline magic 00 ff ff 00 fe fe fe fe fd fd fd fd 12 34 56 78
IPv4 address 04, four one's-complement address octets, big-endian u16 port; 7 bytes total
IPv6 address 06, little-endian i16 family value 23, big-endian port, big-endian 32-bit flow value, 16 address bytes, big-endian 32-bit scope value; 29 bytes total

For example, 127.0.0.1:19132 encodes as 04 80 ff ff fe 4a bc. The current IPv6 writer emits zero flow/scope values. This address-field support does not establish interoperability with every scoped IPv6 deployment.

Sequence comparisons use serial arithmetic: a nonzero forward difference smaller than 2²³ is newer. The next value after ff ff ff is 00 00 00. Receive validation further bounds forward jumps; comparing these indexes as ordinary monotonically increasing integers is incorrect.

Discovery and offline handshake

The listener accepts RakNet protocol 10 and 11. The default client requests 10; connect_with_version and connect_with_version_and_mtu select a peer-compatible version. RakNet protocol version and an application's own version are separate fields.

sequenceDiagram
    participant C as Client
    participant S as Listener
    C->>S: 0x01 UnconnectedPing (optional)
    S-->>C: 0x1c UnconnectedPong + MOTD
    C->>S: 0x05 OpenConnectionRequest1 + padding
    S-->>C: 0x06 OpenConnectionReply1 + MTU
    C->>S: 0x07 OpenConnectionRequest2 + address/GUID/MTU
    S-->>C: 0x08 OpenConnectionReply2
    C->>S: Frame set: 0x09 ConnectionRequest
    S-->>C: Frame set: 0x10 ConnectionRequestAccepted
    C->>S: Frame set: 0x13 NewIncomingConnection
    Note over C,S: Connected payload exchange
Loading

Field lists below include the first packet ID and preserve wire order. magic is the 16-byte constant above; addresses use the variable IPv4/IPv6 encoding.

ID Packet Fields after ID
0x01, 0x02 Unconnected ping variants i64 timestamp, magic, u64 client GUID
0x1c Unconnected pong i64 echoed timestamp, u64 server GUID, magic, string MOTD
0x05 Open connection request 1 magic, u8 protocol version, padding
0x06 Open connection reply 1 magic, u64 server GUID, u8 security flag, u16 nominal MTU
0x07 Open connection request 2 magic, server address, u16 nominal MTU, u64 client GUID
0x08 Open connection reply 2 magic, u64 server GUID, client address, u16 nominal MTU, u8 encryption flag
0x12 Already connected magic, u64 server GUID
0x19 Incompatible protocol version u8 supported protocol, magic, u64 server GUID

The 0x19 table describes the outgoing writer. The current reader instead expects magic before the protocol byte; this asymmetric error-packet codec is an interoperability limitation on unsupported-version replies, not a second negotiated layout.

GUIDs identify peers for transport negotiation; they are not authenticated identities. The library's offline reply security/encryption flags are zero. It does not implement the original SDK's secure-handshake variants.

Request 1 probes a nominal MTU by padding: its UDP payload length is MTU − 28; the listener reconstructs the nominal size as received payload length plus 28. Those 28 bytes are the IPv4/UDP convention, also used by this implementation's packet budgeting. This is handshake negotiation, not continuous path-MTU discovery after connection establishment.

Connected handshake and control messages

ID Payload Fields after ID
0x09 Connection request u64 client GUID, i64 request timestamp, u8 security flag
0x10 Connection request accepted client address, u16 system index, internal-address list, i64 request timestamp, i64 accepted timestamp
0x13 New incoming connection server address, internal-address list, i64 request timestamp, i64 accepted timestamp
0x00 Connected ping i64 client timestamp
0x03 Connected pong i64 echoed client timestamp, i64 server timestamp
0x15 Disconnect notification No additional payload
0xfe Application message Application-defined bytes

The serializers write ten internal addresses; parsing accommodates common ten- and twenty-address peer layouts and requires the final 16 timestamp bytes. Offline magic is not repeated in these connected control payloads. The 0x09 → 0x10 → 0x13 exchange establishes connected readiness beyond the offline MTU negotiation.

Local deadlines use monotonic time; wire timestamps are separate wall-clock fields. send waits for handshake readiness. Discovery APIs can obtain MOTD without opening an application connection. The current application receive path only exposes payloads beginning with 0xfe; wrap arbitrary data after that byte rather than choosing another first byte.

Datagrams and encapsulated frames

The decoder accepts frame-set IDs 0x80..=0x8d. The writer uses 0x84, with the continuation bit producing 0x8c for fragments after the first. It accepts this profile's IDs rather than every datagram variant of upstream RakNet.

Frame-set header:
  u8 packet ID
  u24-LE datagram sequence number

Repeated frame records:
  u8 flags
  u16-BE payload length in BITS
  [u24-LE reliable index]              reliable modes
  [u24-LE sequencing index]            sequenced modes
  [u24-LE ordering index + u8 channel] ordered or sequenced modes
  [u32-BE split count + u16-BE split ID + u32-BE fragment index]
  payload bytes

The three high flag bits encode reliability: flags >> 5. Bit 4 (0x10) indicates a split frame. Payload length excludes all headers. The parser requires a byte-aligned bit length and enough bytes for every conditional field.

Mode value Frame metadata, excluding payload One-frame UDP overhead, excluding IP/UDP
0 Unreliable flags + length 7 B
1 UnreliableSequenced flags + length + sequence + order/channel 14 B
2 Reliable flags + length + reliable index 10 B
3 ReliableOrdered flags + length + reliable index + order/channel 14 B
4 ReliableSequenced flags + length + reliable index + sequence + order/channel 17 B

The overhead column includes the 4-byte frame-set header once. Split metadata adds 10 B per frame. Packing several frames amortizes only the frame-set/IP/UDP headers; each frame keeps its own conditional metadata.

Five delivery modes and indexes

Mode Loss behavior Receiver delivery Typical use
Unreliable No reliable retry state Best effort, without order guarantee Disposable telemetry or updates
UnreliableSequenced No reliable retry state Rejects sequence indexes older than the channel's accepted state Position/aim updates that replace stale state
Reliable Retransmitted until ACK or connection failure Reliable, no sender-order guarantee Independent events
ReliableOrdered Retransmitted until ACK or connection failure Consecutive ordering indexes per channel; gaps wait Commands, transactions, file chunks
ReliableSequenced Reliable retry state Reliable transport plus stale-sequence filtering per channel State updates where old state is unnecessary

ReliableSequenced is not a promise that every old update reaches the application. Transport receipt and filtering are distinct. It also does not imply that this sender cancels all older pending retries as soon as new state is sent.

Four counters serve different purposes:

  • Datagram sequence: identifies each transmission for ACK/NACK; a retry gets a new value.
  • Reliable index: identifies a logical reliable frame for deduplication; unchanged by a retry. Each fragment gets its own index.
  • Ordering index: defines sender order within a channel. All fragments of one ordered message share it; successful reassembly occupies one position.
  • Sequencing index: filters stale sequenced messages per channel. Ordering and sequencing counters interact in the channel metadata; this is not global ordering across all modes.

Public channel APIs accept a u8 channel; default sends use channel 0. Ordered/sequenced state is tracked per channel. Independent channels can avoid an ordered gap holding up an unrelated ordered stream, but still share UDP, send capacity and bottleneck bandwidth. Do not assume this u8 range means every other RakNet implementation supports 256 channels. Reliable deduplication accepts forward indexes within a 65,536-index window; overly distant indexes are rejected.

ACK/NACK and loss recovery

ACK (0xc0) and NACK (0xa0) are direct UDP control packets:

u8 ID
u16-BE record count
repeated records:
  u8 single tag                 1 = single, 0 = inclusive range
  u24-LE start
  [u24-LE end]                  only for ranges

For example, ACK of datagram 7 is c0 00 01 01 07 00 00. An inclusive NACK for 7–9 is a0 00 01 00 07 00 00 09 00 00. Wrapped ranges are represented by separate nonwrapping ranges. Invalid record tags, reversed ranges and insufficient bytes are rejected before unsafe allocation or index expansion.

The receiver collects received datagram numbers into ACK ranges and records gaps as NACK ranges. A gap is evidence of possible loss, not proof: reordering can create it too. A missing datagram that arrives before its NACK is emitted removes that hole. ACK means datagram receipt, not application acknowledgement.

The sender has three recovery paths:

  1. Explicit NACK: match the datagram against outstanding reliable frames and queue an immediate retry. A NACK handler flushes eligible retries without waiting for RTO.
  2. ACK-gap fast retry: a valid later ACK advancing the acknowledged sequence can expose earlier outstanding datagrams. Those frames are marked for retry. Grouped ACK ranges account for all acknowledged members before identifying remaining gaps.
  3. Timer fallback: if feedback is lost or a tail packet has no later ACK to expose it, retry when its deadline expires.

A retry keeps the frame's reliable/order/sequence metadata but assigns a fresh datagram number. Up to 64 previous datagram numbers are retained per outstanding frame so a delayed ACK can still release retry state. ACKs outside actual send history do not advance that state. Packed datagrams acknowledge all of their reliable members; unreliable frames are not retained for reliable retries.

The current timing algorithm is intentionally specific to this library:

Initial SRTT = 50 ms; initial base RTO = 50 ms
SRTT = floor((4 × old SRTT + RTT sample) / 5)
base RTO = clamp(floor(1.5 × SRTT), 50 ms, 12,000 ms)
repeat retry deadline: multiply RTO by 1.5 per retry, capped at 12,000 ms

Retransmitted frames do not contribute ambiguous RTT samples (Karn's rule). Deadlines are cached to avoid scanning every outstanding frame on every send. This estimator does not use TCP's RTT-variance formula. ACK-gap retry can produce extra traffic under reordering; timeout backoff does not by itself make the sender an adaptive congestion controller.

Fragmentation and MTU

The nominal MTU defaults to 1400 B; client and listener MTU APIs accept 61–1492 B. Outgoing message payload chunks use negotiated MTU − 60, conservatively reserving headers. At the default MTU the chunk payload budget is 1340 B, including the application's 0xfe byte.

In this implementation, only ReliableOrdered automatically splits an oversized outgoing message. The other four modes return PacketSizeExceedMTU rather than silently converting it to a different reliability mode. If those modes need large logical data, define explicit application chunks and appropriate reconstruction semantics.

Each fragment carries split count, split ID and zero-based fragment index. The receiver deduplicates reliable frames, checks matching group metadata, waits for all indexes, joins payloads in fragment-index order, and then applies ordered delivery. Duplicate fragments do not complete a group twice. A fragment's ACK releases transport retry state even while the full message waits for reassembly.

Implementation limits: 65,536 fragments per group; 1,024 simultaneous reassembly groups; a 64 MiB reassembly accounting cap, including 128 B of estimated metadata per frame. Groups with no new fragment progress for 60 seconds expire; current connection maintenance closes the connection rather than promising partial-message delivery. Split IDs are 16-bit values; applications should not treat them as unique long-term message IDs.

Batching and local scheduling

send does not introduce a timer to accumulate small messages. Explicit batch APIs can pack already available, nonfragmented ReliableOrdered frames: at most eight frames per datagram, and only within the negotiated packet budget. Each logical message still has its own indexes and boundary. Other modes and fragmented messages use separate datagrams. A NACK or retry timeout disables coalescing for the rest of that connection, retaining the ordinary loss-recovery path.

The default reliable flight window is 64 frames, not 64 application messages or 64 packed datagrams. One fragmented message consumes several slots; a packed datagram also consumes one slot per reliable frame.

The optional send-policy feature adds local frame/encoded-byte flight limits, reliable and unreliable queue budgets, and per-flush byte budgets. Applying set_send_options activates it per connection. It sends existing wire formats without peer negotiation. Separate pending queues preserve unreliable admission capacity, but neither reserve nor fair scheduling guarantees network bandwidth or delivery. There is no automatic bitrate estimate, adaptive congestion window, fixed-rate pacer or accepted-unreliable-message eviction. See Send policy for exact settings and measured costs.

Lifecycle, limits and security

  • Default send accounting has a 256 KiB admission high-water mark and a 64 MiB hard cap with 128 B estimated overhead per frame. Larger permitted ordered sends can exceed the soft high-water mark. These figures are not RSS limits.
  • Ordered receive buffering is capped at 65,536 messages and 64 MiB of payload. The application-delivery channel and pending payloads also have separate bounds; keep receiving while sending bulk data. Transport ACKs must not be used as business-level completion signals.
  • flush waits for the send queue, including reliable acknowledgement state, to drain; it is not proof of application processing. close/peer disconnect and receive timeouts terminate the connection and can cause operations to fail.
  • Connection maintenance normally runs on a 50 ms cadence with wakeups for new work. Explicit loss feedback can trigger retries immediately. Idle maintenance is opt-in and may sleep after 500 ms of quiet with empty queues; incoming work wakes it. Current receive inactivity timeout is 60 seconds.
  • No transport TLS, authenticated GUID, cryptographic ACK authentication, original SDK security handshake, congestion fairness guarantee, NAT traversal plugin or NetherNet support is supplied. Use trusted deployment boundaries or application-layer security as required.
  • Original RakNet also defines acknowledgement-receipt modes and SDK priority/plugin features. rust-raknet's public reliability enum supports the five values 0–4 only. Do not infer support for values 5–7 from their existence upstream.

Interoperability checklist and source map

Check protocol version, nominal MTU convention, supported datagram IDs, channel range, ten/twenty internal-address layouts, endian rules, payload prefix and fragmentation behavior. Confirm actual message exchange with the target peer; successful offline ping alone does not test the connected handshake or delivery semantics. A terminating proxy creates two independent RakNet connections and regenerates transport metadata for each leg.

Topic Source module in the matching checkout
Packet IDs, handshake and ACK/NACK codecs src/packet.rs
Integer/string/address codecs and offline magic src/datatype.rs
Frame layout, receive queues, ACK gaps, retries and packing src/arq.rs
Serial arithmetic and reliable deduplication src/sequence.rs
Reassembly, metadata validation and limits src/fragment.rs
Connection tasks, application prefix, feedback and lifecycle src/socket.rs
Version/MTU negotiation, discovery and acceptance src/server.rs, src/utils.rs
Optional policy limits src/send_options.rs
Platform UDP fast paths src/udp.rs

The upstream SDK's PacketPriority.h, MessageIdentifiers.h and ReliabilityLayer.cpp provide primary references for the original reliability enumeration, message identifiers and reliability machinery. The numeric layouts, limits and scheduling behavior above were checked against this project's source; upstream behavior is not assumed to be identical.

Optional recovery scheduling (1.2.0)

Recovery policy adds deadline wakeups, complete-tail-datagram probes and RTT-progress backoff without changing these packet formats. All options are off by default; compiling the feature also corrects subsequent send timestamps after feedback processing. The base RTO estimator and ordinary timeout/NACK fallback remain unchanged.

Clone this wiki locally