Repository navigation
Protocol Reference EN
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.
- Layers and terminology
- Primitive encodings
- Discovery and offline handshake
- Connected handshake and control messages
- Datagrams and encapsulated frames
- Five delivery modes and indexes
- ACK/NACK and loss recovery
- Fragmentation and MTU
- Batching and local scheduling
- Lifecycle, limits and security
- Interoperability checklist and source map
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 | 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.
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
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.
| 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.
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.
| 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 (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:
- Explicit NACK: match the datagram against outstanding reliable frames and queue an immediate retry. A NACK handler flushes eligible retries without waiting for RTO.
- 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.
- 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.
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.
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.
- 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.
-
flushwaits 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.
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.
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.
English · 简体中文 · GitHub · crates.io 1.4.0 · Reviewed / 核对:2026-10-12 / b909c93
- Overview
- Quick start
- Blocking calls and shared runtimes
- Public API index
- Five reliability modes
- RakNet wire format and recovery
- Message lifecycle and limits
- Bytes and explicit batching
- Send policy
- Recovery policy and tail probes
- Every configuration parameter
- Concurrency and socket tuning
- Errors, deadlines and cancellation
- Congestion and deployment boundaries
- Benchmark results
- Benchmark methodology
- Benchmark reproduction
- Minecraft Bedrock proxy
- Troubleshooting and development
- Release history and upgrading