Frame Reader and Sender¶
FrameReader and FrameSender are the internal workhorses of the Nalix.SDK transport layer. They manage the low-level serialization of frames, socket I/O, and payload transformations, abstracting these complexities away from the TcpSession.
Internal Pipeline¶
graph TD
subgraph Outbound [Outbound Pipeline]
S[TcpSession.SendAsync] --> FS[FrameSender]
FS --> T[Transform: Encrypt/Compress]
T --> G{Payload >= FragmentOptions.MaxChunkSize?}
G -->|No| F[2-byte length-prefixed TCP frame]
G -->|Yes| C[FragmentHeader chunks]
F --> L[SemaphoreSlim send lock]
C --> L
L --> SK[Socket.SendAsync]
end
subgraph Inbound [Inbound Pipeline]
RSK[Socket.ReceiveAsync] --> FR[FrameReader]
FR --> H[Read 2-byte little-endian length]
H --> V[Validate length against TransportOptions.BufferSize]
V --> L2[Read exact payload bytes]
L2 --> FA[Fragment reassembly]
FA --> IT[Inbound transform]
IT --> Callback[Forward to Session]
end
Source mapping¶
src/Nalix.SDK/Transport/Internal/FrameReader.cssrc/Nalix.SDK/Transport/Internal/FrameSender.cssrc/Nalix.Codec/Transforms/FramePipeline.cs
Frame Sender (FrameSender)¶
The FrameSender provides a thread-safe, ordered outbound pipeline. The current implementation does not use an intermediate channel queue. Instead, it applies transforms, frames the payload, then serializes socket writes with a SemaphoreSlim send lock.
- Strict ordering: concurrent send calls are serialized by
_sendLock, so frame bytes cannot interleave on the socket. - Direct socket writes: after the caller enters the send lock, the sender loops until the full frame has been written with
Socket.SendAsync. - Automatic fragmentation: payloads whose transformed length is greater than or equal to
FragmentOptions.MaxChunkSizeare split intoFragmentHeaderchunks. - Frame-size guardrails: normal TCP frames must fit in the 2-byte length prefix (
ushort.MaxValue). Fragment chunks must also fit after adding the TCP header andFragmentHeader.WireSize. - Transformation: integrates with the centralized
FramePipelineto encrypt and compress payloads before framing. - Error contract: non-fatal send exceptions invoke the configured error callback and cause
SendAsyncto returnfalse. - Pooled memory: temporary frame buffers are rented from
BufferLease.ByteArrayPooland returned after each send attempt.
Frame Reader (FrameReader)¶
The FrameReader manages the long-running socket receive loop. It is responsible for reassembling protocol frames from the raw TCP stream.
- Header parsing: reads the 2-byte little-endian length prefix to determine the total frame length.
- Length validation: rejects frames smaller than
TcpSession.HeaderSizeor larger thanTransportOptions.BufferSizewith a cachedSocketError.MessageSizeexception. - Exact reads: repeatedly calls
Socket.ReceiveAsyncuntil the requested header or payload span is filled; a zero-byte receive is treated asConnectionReset. - Fragment management: synchronized with the sender's chunking logic to reassemble fragmented payloads before delivering them upward.
- Transform application: integrates with the centralized
FramePipelineto decrypt and decompress payloads in-place. - Ownership handoff: wraps each frame in a
BufferLeaseand forwards the processed lease to the session callback.
Ownership and Performance¶
A critical aspect of the SDK pipeline is its zero-copy (or minimized copy) architecture.
- Renting: Buffers are rented from the
ArrayPool<byte>via theBufferLeaseabstraction. - Transformation: LZ4 decompression and AEAD decryption are performed directly on these rented blocks.
- Dispatch: The final lease is delivered to the user's
OnMessageReceivedhandler. - Cleanup: The user must dispose of the lease (usually via
using var) to return the memory to the pool.