pub trait AsyncUdpSocket:
Send
+ Sync
+ Debug
+ 'static {
// Required methods
fn local_addr(&self) -> Result<SocketAddr>;
fn poll_send(
&self,
cx: &mut Context<'_>,
transmit: &Transmit<'_>,
) -> Poll<Result<usize>>;
fn poll_recv(
&self,
cx: &mut Context<'_>,
bufs: &mut [IoSliceMut<'_>],
meta: &mut [RecvMeta],
) -> Poll<Result<usize>>;
// Provided methods
fn max_gso_segments(&self) -> usize { ... }
fn max_gro_segments(&self) -> usize { ... }
fn send_to<'a>(
&'a self,
buf: &'a [u8],
target: SocketAddr,
) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'a>> { ... }
fn recv_from<'a>(
&'a self,
buf: &'a mut [u8],
) -> Pin<Box<dyn Future<Output = Result<(usize, SocketAddr)>> + Send + 'a>> { ... }
}Expand description
Abstract implementation of a UDP socket for runtime independence.
§Poll-based, by design
The two packet primitives — poll_send and
poll_recv — are synchronous and readiness-based, mirroring
quinn’s AsyncUdpSocket. This is what keeps the packet path allocation-free: a boxed
future per datagram would cost one heap allocation per send and per receive, and callers
that merely want to test readiness (see the driver’s burst drain) would allocate just
to discard.
An implementor supplies local_addr and the two poll primitives;
everything else is defaulted. The two async methods (send_to,
recv_from) are single-datagram conveniences for control-plane use,
written over the primitives — they each box a future, so the packet path polls instead.
Batching has no convenience wrappers by design: set Transmit::segment_size for
poll_send, and on the receive side pass more than one buffer to poll_recv and read
RecvMeta::stride from each message it fills.
Required Methods§
Sourcefn local_addr(&self) -> Result<SocketAddr>
fn local_addr(&self) -> Result<SocketAddr>
Get the local address this socket is bound to
Sourcefn poll_send(
&self,
cx: &mut Context<'_>,
transmit: &Transmit<'_>,
) -> Poll<Result<usize>>
fn poll_send( &self, cx: &mut Context<'_>, transmit: &Transmit<'_>, ) -> Poll<Result<usize>>
Attempt to send transmit, registering cx’s waker if the socket is not writable
yet.
contents goes to destination as a
single datagram when segment_size is None. Some(n)
requests UDP GSO: the buffer is split into consecutive n-byte datagrams (the last
may be shorter) and emitted in one syscall. Callers should only supply contents
spanning more than one segment when max_gso_segments
reports > 1 — a socket reporting 1 may ignore segment_size and emit one
oversized datagram.
ecn, when Some, stamps the ECN codepoint bits on every segment.
Returns the number of payload bytes accepted.
Sourcefn poll_recv(
&self,
cx: &mut Context<'_>,
bufs: &mut [IoSliceMut<'_>],
meta: &mut [RecvMeta],
) -> Poll<Result<usize>>
fn poll_recv( &self, cx: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>], meta: &mut [RecvMeta], ) -> Poll<Result<usize>>
Attempt to receive up to bufs.len() messages, registering cx’s waker if none is
ready.
Fills bufs[..n] and meta[..n] and returns n, never more than
bufs.len().min(meta.len()). Two independent kinds of batching are in play:
- Multiple messages per syscall. Where the platform offers
recvmmsg(Linux) or an equivalent, one call returns up toBATCH_SIZEdatagrams from different peers. Elsewherenis always 1, so a caller must never assume more. - GRO coalescing within a message. Each filled buffer may itself hold several
consecutive same-flow datagrams — see
RecvMeta::stride.
Implementations must report a stride of at least 1 for every filled message:
quinn-udp mirrors len into stride, which is 0 for a zero-length datagram and
would make de-segmentation divide by zero.
A minimal implementation may fill only bufs[0] and return Ok(1).
§Errors
Return the error rather than retrying inside the implementation: the driver
classifies it and resumes the receive loop for anything transient. A socket serving
many peers learns about per-peer failures through the socket itself, so
ConnectionRefused (how an ICMP port-unreachable surfaces on Linux),
ConnectionReset (the same on Windows), Interrupted, WouldBlock and TimedOut
are all treated as transient and do not tear the socket down. Anything else is taken
to mean the socket is unusable.
Provided Methods§
Sourcefn max_gso_segments(&self) -> usize
fn max_gso_segments(&self) -> usize
Maximum number of segments a single poll_send call can emit in
one syscall via UDP GSO. Returns 1 when GSO is unavailable (each segment then costs
one syscall).
Callers must consult this before passing a segment_size that spans more than one
datagram: a socket reporting 1 may ignore segment_size and emit one oversized
datagram.
Sourcefn max_gro_segments(&self) -> usize
fn max_gro_segments(&self) -> usize
Maximum number of datagrams the kernel may coalesce into a single message via
UDP GRO — not into a whole poll_recv call, which may return up
to BATCH_SIZE messages. Returns 1 when GRO is unavailable.
§Buffer sizing
This value decides how large a buffer the driver hands to
poll_recv: reporting n > 1 asks for roughly n × 1500 bytes
per message, because a coalesced segment is bounded by the path MTU, not by the
largest datagram the application sends. Sizing against your own maximum datagram
size instead is the easy mistake, and it fails quietly — the kernel drops the tail of
a coalesced read, which looks like unexplained packet loss rather than an error.
Two consequences worth knowing before reporting > 1:
- Buffers are allocated per socket per receive, so the count is an allocation
multiplier. It is clamped internally, and
1500is assumed per segment; paths with an MTU above 1500 are not supported for GRO and would truncate. - The exact formula is the driver’s own policy and may change. Report what the socket can actually coalesce and let the driver size accordingly — an implementation that allocates its own receive buffers to some other rule (a shared socket demultiplexed across connections, say) is responsible for the same MTU bound in its own loop.
Sourcefn send_to<'a>(
&'a self,
buf: &'a [u8],
target: SocketAddr,
) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'a>>
fn send_to<'a>( &'a self, buf: &'a [u8], target: SocketAddr, ) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'a>>
Send buf as a single datagram to target.
Sourcefn recv_from<'a>(
&'a self,
buf: &'a mut [u8],
) -> Pin<Box<dyn Future<Output = Result<(usize, SocketAddr)>> + Send + 'a>>
fn recv_from<'a>( &'a self, buf: &'a mut [u8], ) -> Pin<Box<dyn Future<Output = Result<(usize, SocketAddr)>> + Send + 'a>>
Receive a single datagram from the socket.
Convenience over poll_recv for control-plane callers wanting one
datagram and no batching. It discards RecvMeta::stride, so do not use it where
GRO coalescing is possible — the packet path polls instead.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".