Implement native UDP transport reliability (#52)
This commit is contained in:
77
docs/udp-transport.md
Normal file
77
docs/udp-transport.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# Native UDP transport
|
||||
|
||||
`libremetaverse::UDPBase` is the native Tokio implementation of the
|
||||
LibreMetaverse UDP transport slice. Construction is runtime-neutral: it does
|
||||
not bind a socket, start a thread, or create an executor. Call `start` from an
|
||||
entered Tokio runtime after constructing either a client transport with
|
||||
`UDPBase::client` or a server transport with `UDPBase::server`.
|
||||
|
||||
The implementation uses three bounded queues:
|
||||
|
||||
- one socket receive task writes complete datagrams to the receive queue with
|
||||
drop-on-full backpressure;
|
||||
- one coordinator validates packet framing, owns per-peer sequence, duplicate,
|
||||
pending-ACK, and reliable resend state, and writes prepared datagrams to a
|
||||
bounded writer queue;
|
||||
- one writer task is the only socket send owner and applies the per-category
|
||||
token buckets before calling `send_to`.
|
||||
|
||||
The default limits mirror the golden C# settings: 512 receive and command
|
||||
entries, a 1,000-entry packet archive, ten ACKs before an immediate standalone
|
||||
ACK, a 500 ms network tick, a 4 second resend timeout, and three retries.
|
||||
Additional explicit limits bound the send queue, reliable window, ACK queue,
|
||||
peer table, decoded zerocode buffer, and all allocations derived from incoming
|
||||
datagrams. Client-mode source validation matches the reference by accepting
|
||||
only the configured simulator IP address; replies continue to use the
|
||||
configured endpoint.
|
||||
|
||||
## Wire behavior
|
||||
|
||||
Every newly prepared packet receives the next four-byte big-endian sequence,
|
||||
using the full `u32` header width and wrapping through zero like the C#
|
||||
`Interlocked.Increment(Int32)` plus `uint` cast. Reliable packets remain in the
|
||||
bounded per-peer window until either an appended ACK or `PacketAck` block
|
||||
removes them. Timed-out entries are retransmitted with `MSG_RESENT` and the
|
||||
same sequence, then removed after the configured retry count. Reliable incoming
|
||||
packets are ACKed even when duplicate; duplicate callbacks are suppressed.
|
||||
Out-of-order packets are delivered immediately, as in the C# client, while
|
||||
gaps and late arrivals are counted separately.
|
||||
|
||||
Pending ACKs are appended in big-endian form while the strict reference
|
||||
`data_length + 5 < MTU` condition holds. Remaining ACKs are emitted as an
|
||||
unreliable `PacketAck`. Zerocoding occurs before ACK appending. If zerocoding
|
||||
would expand a packet beyond the 1,200-byte protocol MTU, the zerocode flag is
|
||||
cleared and the original packet is sent. The transport does not invent an
|
||||
application fragmentation format: packet codecs must use their existing
|
||||
`ToBytesMultiple` behavior, and an oversized unsplit outbound payload returns
|
||||
`UdpTransportError::MtuExceeded`.
|
||||
|
||||
Incoming storage is 4 KiB, matching `UDPPacketBuffer.DEFAULT_BUFFER_SIZE`, and
|
||||
the receiver allocates one extra detection byte so an oversized datagram is
|
||||
rejected rather than silently treated as complete. Packet parsing and
|
||||
zerodecoding use fixed configured ceilings. Malformed or unknown packets are
|
||||
counted and discarded without invoking application callbacks.
|
||||
|
||||
## Throttling, cancellation, and diagnostics
|
||||
|
||||
`AgentThrottle` reproduces all seven C# clamps and its 28-byte little-endian
|
||||
wire layout. Outgoing control/handshake packets bypass throttling; task,
|
||||
texture, and asset packets use independent 100 ms token buckets with the same
|
||||
four-period burst and 200-byte minimum replenishment policy as the reference.
|
||||
`update_throttle` swaps all three buckets in writer order.
|
||||
|
||||
All tasks share a linked cancellation source. `stop_async` cancels and joins
|
||||
the receive, coordinator, and writer tasks; `stop` and final drop cancel and
|
||||
abort them for synchronous C# compatibility. A stopped transport can be
|
||||
started again with fresh task state unless its parent token is cancelled.
|
||||
|
||||
`UdpTransportStats` contains counts and byte totals only. The transport does
|
||||
not log datagram bodies, credentials, capability URLs, or socket error text;
|
||||
its `Debug` implementation also omits endpoint values. The implementation uses
|
||||
only portable Rust and Tokio socket APIs on Linux, Windows, and macOS.
|
||||
|
||||
The isolated loopback suite is:
|
||||
|
||||
```sh
|
||||
cargo test -p libremetaverse --test udp_transport --no-default-features
|
||||
```
|
||||
Reference in New Issue
Block a user