Struct Client
pub struct Client<S> {
state: ConnectionState,
session: S,
capabilities: ServerCapabilities,
keep_alive: u16,
send_quota: u16,
recv_quota: u16,
recv_max: u16,
next_packet_id: PacketId,
clean_start: bool,
inbound_alias_max: Option<NonZeroU16>,
}Expand description
Sans-io MQTT v5 client state machine.
The caller is responsible for:
- Encoding outgoing packets and writing them to the transport.
- Decoding incoming packets and feeding them to
handle_packet. - Driving timers for keep-alive (see
keep_alive).
Fields§
§state: ConnectionState§session: S§capabilities: ServerCapabilities§keep_alive: u16Keep-alive interval in seconds as negotiated.
send_quota: u16Number of QoS > 0 publishes we can still send before hitting the
server’s Receive Maximum.
recv_quota: u16Number of QoS > 0 messages the client can still accept from the
server before the server would violate our Receive Maximum.
recv_max: u16The Receive Maximum value we advertise in CONNECT (§3.1.2.11.4).
u16::MAX (65 535) means we never sent the property.
next_packet_id: PacketIdMonotonic packet ID counter (1..=65535, wraps around skipping 0).
clean_start: boolWhether the last CONNECT used Clean Start = 1.
inbound_alias_max: Option<NonZeroU16>The Topic Alias Maximum we advertise in CONNECT (§3.1.2.11.5).
None (the default) means inbound topic aliases are not permitted.
Implementations§
§impl<S: SessionState> Client<S>
impl<S: SessionState> Client<S>
pub fn poll_transmit<B, A>(
&mut self,
out: &mut PacketWriter<B>,
aliases: &mut A,
on_event: &mut dyn FnMut(TransmitEvent),
)where
B: ByteBuffer,
A: TopicAliasTable,
pub fn poll_transmit<B, A>(
&mut self,
out: &mut PacketWriter<B>,
aliases: &mut A,
on_event: &mut dyn FnMut(TransmitEvent),
)where
B: ByteBuffer,
A: TopicAliasTable,
Drains pending SUBSCRIBE/UNSUBSCRIBE groups, then the outbox and
pending PUBRELs, onto out: the single outbound transmit path.
Subscriptions go first so they precede publishes accepted together
with them.
Resolves topic aliases via aliases, enforces the server’s Maximum
Packet Size (§3.1.2.11.6), and stays within the send quota. Every
outcome is reported through on_event; an over-large packet (or
whole group) is dropped as TooLarge. If
out cannot accept a packet (fixed-capacity buffer full) the drain
stops and retries on the next call.
fn drain_subscription_groups<B>(
&mut self,
out: &mut PacketWriter<B>,
max_packet_size: Option<u32>,
on_event: &mut dyn FnMut(TransmitEvent),
)where
B: ByteBuffer,
fn drain_subscription_groups<B>(
&mut self,
out: &mut PacketWriter<B>,
max_packet_size: Option<u32>,
on_event: &mut dyn FnMut(TransmitEvent),
)where
B: ByteBuffer,
Sends every pending SUBSCRIBE/UNSUBSCRIBE group not already in flight,
oldest first, marking each in flight once written. A group exceeding
max_packet_size can never be sent: it is removed and reported as
TooLarge so it cannot wedge the queue.
§impl<S: SessionState> Client<S>
impl<S: SessionState> Client<S>
pub fn new(session: S) -> Self
pub fn new(session: S) -> Self
Creates a new client with the given session state backend.
pub const fn connection_state(&self) -> ConnectionState
pub const fn connection_state(&self) -> ConnectionState
Current connection state.
pub const fn capabilities(&self) -> &ServerCapabilities
pub const fn capabilities(&self) -> &ServerCapabilities
Server capabilities from the most recent CONNACK.
pub const fn keep_alive(&self) -> u16
pub const fn keep_alive(&self) -> u16
Effective keep-alive interval in seconds.
pub const fn send_quota(&self) -> u16
pub const fn send_quota(&self) -> u16
Remaining send quota for QoS > 0 publishes.
pub const fn session_mut(&mut self) -> &mut S
pub const fn session_mut(&mut self) -> &mut S
Mutable reference to the session state backend.
pub fn connect_sent(&mut self, connect: &Connect<'_>) -> Result<(), ClientError>
pub fn connect_sent(&mut self, connect: &Connect<'_>) -> Result<(), ClientError>
Records that connect has been sent.
Mirrors the Clean Start flag, keep-alive, Receive Maximum (§3.1.2.11.3), and Topic Alias Maximum (§3.1.2.11.5) from the packet that actually went on the wire.
§Errors
Returns ClientError::InvalidState if the client is not
disconnected.
pub fn connection_lost(&mut self)
pub fn connection_lost(&mut self)
Notifies the state machine that the transport has been lost.
Resets connection-level state (quotas, capabilities) while preserving
session state for reconnection. In-flight SUBSCRIBE/UNSUBSCRIBE entries
are kept and re-sent on the next connect: the per-connection in-flight
markers are cleared by
reset_in_flight when
the next CONNACK arrives.
pub fn session_lost(&mut self, on_failed_pubrel: &mut dyn FnMut(PacketId))
pub fn session_lost(&mut self, on_failed_pubrel: &mut dyn FnMut(PacketId))
Discards the session state the server forgot (§4.1).
Removes every PUBREL-pending QoS 2 flow (reporting each id through
on_failed_pubrel so the driver can fail its future), resets outbox
was_sent flags (DUP = 0), and clears incoming QoS 2 tracking.
Confirmed subscriptions are preserved; re-issuing them is the driver’s
policy.
The I/O driver must call this on a CONNACK with
session_present: false, before draining the session onto the new
connection.
pub fn handle_packet<'a>(
&mut self,
packet: &Packet<'a>,
) -> Result<HandleOutcome<'a>, ClientError>
pub fn handle_packet<'a>( &mut self, packet: &Packet<'a>, ) -> Result<HandleOutcome<'a>, ClientError>
Processes an incoming decoded packet from the server.
Protocol violations by the server are reported as a
Response::Disconnect to send (§4.13), not as an error.
§Errors
Returns ClientError::InvalidState if the packet is not valid in
the current connection state (e.g. a CONNACK while not connecting).
fn handle_connack<'a>( &mut self, connack: &Connack<'a>, ) -> Result<HandleOutcome<'a>, ClientError>
fn parse_capabilities(props: &ConnackProperties) -> ServerCapabilities
fn handle_publish<'a>( &mut self, publish: &Publish<'a>, ) -> Result<HandleOutcome<'a>, ClientError>
fn require_connected(&self) -> Result<(), ClientError>
fn require_connected(&self) -> Result<(), ClientError>
Errors with ClientError::InvalidState unless the client is
currently connected.
fn handle_puback<'a>( &mut self, ack: Ack<'_>, ) -> Result<HandleOutcome<'a>, ClientError>
fn handle_pubrec<'a>( &mut self, ack: Ack<'_>, ) -> Result<HandleOutcome<'a>, ClientError>
fn handle_pubrel<'a>( &mut self, ack: Ack<'_>, ) -> Result<HandleOutcome<'a>, ClientError>
fn handle_pubcomp<'a>( &mut self, ack: Ack<'_>, ) -> Result<HandleOutcome<'a>, ClientError>
fn handle_suback<'a>( &mut self, suback: &Suback<'a>, ) -> Result<HandleOutcome<'a>, ClientError>
fn handle_unsuback<'a>( &mut self, unsuback: &Unsuback<'a>, ) -> Result<HandleOutcome<'a>, ClientError>
fn handle_pingresp<'a>(&self) -> Result<HandleOutcome<'a>, ClientError>
const fn handle_disconnect<'a>( &mut self, disconnect: &Disconnect<'a>, ) -> Result<HandleOutcome<'a>, ClientError>
const fn handle_auth<'a>(&mut self) -> Result<HandleOutcome<'a>, ClientError>
pub fn prepare_publish(
&mut self,
qos: QoS,
topic: &Topic,
payload: &[u8],
retain: bool,
properties: &PublishProperties,
) -> Result<PacketId, ClientError>
pub fn prepare_publish( &mut self, qos: QoS, topic: &Topic, payload: &[u8], retain: bool, properties: &PublishProperties, ) -> Result<PacketId, ClientError>
Allocates a packet identifier and stores an outgoing QoS 1 or QoS 2
publish in the session for potential retransmission.
Returns the allocated packet_id. The caller must construct and send
the PUBLISH packet using this ID. The packet_id doubles as the
stable handle the driver uses to correlate the eventual
PUBACK/PUBCOMP with the user-facing completion future (§4.4).
§Errors
Returns ClientError::InvalidState if not connected,
ClientError::InvalidQoS if qos is QoS::AtMostOnce,
ClientError::QoSNotSupported or ClientError::RetainNotAvailable
if the server forbids the request, or ClientError::PacketIdExhausted
if all packet identifiers are in use.
This does not consume send quota: the message is stored in the
outbox and the send quota is consumed only when it is drained onto the
wire by poll_transmit, so the Receive Maximum
bounds in-flight messages rather than how many may be queued.
pub fn store_offline_publish(
&mut self,
qos: QoS,
topic: &Topic,
payload: &[u8],
retain: bool,
properties: &PublishProperties,
) -> Result<PacketId, ClientError>
pub fn store_offline_publish( &mut self, qos: QoS, topic: &Topic, payload: &[u8], retain: bool, properties: &PublishProperties, ) -> Result<PacketId, ClientError>
Stores an outgoing QoS 1 or QoS 2 publish while not connected, so it
can be drained when a connection is next established.
Unlike prepare_publish, this works in any
connection state and applies no capability checks (the server’s are
unknown while offline); the message is recorded as durable session state
immediately. qos must be QoS::AtLeastOnce or QoS::ExactlyOnce.
§Errors
Returns ClientError::InvalidQoS if qos is QoS::AtMostOnce, or
ClientError::PacketIdExhausted if all packet identifiers are in use.
fn store_publish_entry(
&mut self,
qos: QoS,
topic: &Topic,
payload: &[u8],
retain: bool,
properties: &PublishProperties,
) -> Result<PacketId, ClientError>
fn store_publish_entry( &mut self, qos: QoS, topic: &Topic, payload: &[u8], retain: bool, properties: &PublishProperties, ) -> Result<PacketId, ClientError>
Allocates a packet id and records an outbox entry, the shared core of the connected and offline publish-store paths.
pub fn prepare_subscribe<'a>(
&mut self,
filters: impl IntoIterator<Item = (&'a TopicFilter, SubscriptionOptions)>,
subscription_id: Option<u32>,
) -> Result<PacketId, ClientError>
pub fn prepare_subscribe<'a>( &mut self, filters: impl IntoIterator<Item = (&'a TopicFilter, SubscriptionOptions)>, subscription_id: Option<u32>, ) -> Result<PacketId, ClientError>
Allocates a packet identifier for a SUBSCRIBE and records each filter as an in-flight subscription under it.
filters are the topic filters and their options; subscription_id is
the optional SUBSCRIBE-level identifier. The caller builds and sends the
SUBSCRIBE using the returned id; the entries resolve when
handle_packet sees the matching SUBACK.
§Errors
Returns ClientError::InvalidState if not connected, or
ClientError::PacketIdExhausted if all identifiers are in use.
pub fn store_offline_subscribe<'a>(
&mut self,
filters: impl IntoIterator<Item = (&'a TopicFilter, SubscriptionOptions)>,
subscription_id: Option<u32>,
) -> Result<PacketId, ClientError>
pub fn store_offline_subscribe<'a>( &mut self, filters: impl IntoIterator<Item = (&'a TopicFilter, SubscriptionOptions)>, subscription_id: Option<u32>, ) -> Result<PacketId, ClientError>
Records an in-flight SUBSCRIBE while not connected, so it is sent when a connection is next established.
Like prepare_subscribe but works in any
connection state and applies no capability checks (the server’s are
unknown while offline).
§Errors
Returns ClientError::PacketIdExhausted if all identifiers are in
use.
fn record_subscribe<'a>(
&mut self,
require_connected: bool,
filters: impl IntoIterator<Item = (&'a TopicFilter, SubscriptionOptions)>,
subscription_id: Option<u32>,
) -> Result<PacketId, ClientError>
fn record_subscribe<'a>( &mut self, require_connected: bool, filters: impl IntoIterator<Item = (&'a TopicFilter, SubscriptionOptions)>, subscription_id: Option<u32>, ) -> Result<PacketId, ClientError>
Allocates a packet id and records each filter as an in-flight SUBSCRIBE,
the shared core of the connected
(prepare_subscribe) and offline
(store_offline_subscribe) paths;
require_connected gates the connected variant on
ConnectionState::Connected.
pub fn prepare_unsubscribe<'a>(
&mut self,
filters: impl IntoIterator<Item = &'a TopicFilter>,
) -> Result<PacketId, ClientError>
pub fn prepare_unsubscribe<'a>( &mut self, filters: impl IntoIterator<Item = &'a TopicFilter>, ) -> Result<PacketId, ClientError>
Allocates a packet identifier for an UNSUBSCRIBE and records each filter as in-flight unsubscribing under it.
The entries resolve when handle_packet sees the matching UNSUBACK.
§Errors
Returns ClientError::InvalidState if not connected, or
ClientError::PacketIdExhausted if all identifiers are in use.
pub fn store_offline_unsubscribe<'a>(
&mut self,
filters: impl IntoIterator<Item = &'a TopicFilter>,
) -> Result<PacketId, ClientError>
pub fn store_offline_unsubscribe<'a>( &mut self, filters: impl IntoIterator<Item = &'a TopicFilter>, ) -> Result<PacketId, ClientError>
Records an in-flight UNSUBSCRIBE while not connected.
§Errors
Returns ClientError::PacketIdExhausted if all identifiers are in
use.
fn record_unsubscribe<'a>(
&mut self,
require_connected: bool,
filters: impl IntoIterator<Item = &'a TopicFilter>,
) -> Result<PacketId, ClientError>
fn record_unsubscribe<'a>( &mut self, require_connected: bool, filters: impl IntoIterator<Item = &'a TopicFilter>, ) -> Result<PacketId, ClientError>
Allocates a packet id and records each filter as an in-flight
UNSUBSCRIBE, the shared core of the connected
(prepare_unsubscribe) and offline
(store_offline_unsubscribe) paths.
pub fn cancel_outgoing_qos1(&mut self, packet_id: PacketId)
pub fn cancel_outgoing_qos1(&mut self, packet_id: PacketId)
Cancels a QoS 1 publish previously prepared by
prepare_publish.
Removes the session entry. Send quota is restored only if the entry had already been put on the wire, since a prepared-but-never-drained publish consumed no slot; crediting it would let the in-flight window exceed the server’s Receive Maximum (§4.9). Use when the publish failed to be enqueued/transmitted and the driver wants to release the reservation without going through the wire/PUBACK round trip.
pub fn cancel_outgoing_qos2(&mut self, packet_id: PacketId)
pub fn cancel_outgoing_qos2(&mut self, packet_id: PacketId)
Cancels a QoS 2 publish previously prepared by
prepare_publish.
Removes the session entry. As with
cancel_outgoing_qos1, send quota is
restored only if the entry had already been put on the wire.
pub const fn check_subscribe_capabilities(
&self,
has_wildcard: bool,
has_sub_id: bool,
has_shared: bool,
) -> Result<(), ClientError>
pub const fn check_subscribe_capabilities( &self, has_wildcard: bool, has_sub_id: bool, has_shared: bool, ) -> Result<(), ClientError>
Checks whether a subscribe operation is compatible with server capabilities.
§Errors
Returns an error if the subscribe would violate a server capability.
pub fn drain_outbox<F>(&mut self, send: F)
pub fn drain_outbox<F>(&mut self, send: F)
Drains every outbox publish not yet on the wire this connection through
send, in packet-id order, consuming one unit of send quota per
DrainAction::Sent and stopping when the quota is exhausted (§4.9).
This is the lower-level primitive behind
poll_transmit: fresh publishes, retransmits
after a resume, and the post-reconnect flush all funnel through it, so
the in-flight window can never exceed the server’s Receive Maximum. The
caller’s send does the wire work and returns a DrainAction:
Sent consumes quota and marks the entry in
flight, Discard drops it (the caller has
failed its future), and Stop leaves it for a
later drain (e.g. the output buffer is full). The client owns the
quota, the DUP flag, and the in-flight marker.
pub fn drain_pubrel<F>(&mut self, send: F)
pub fn drain_pubrel<F>(&mut self, send: F)
Resends PUBREL for every QoS 2 publish awaiting PUBCOMP that has not
gone out this connection (only relevant after a session resume), through
send, consuming send quota like drain_outbox.