Skip to content

한국어 | English

Reference index

03. Sockets

This category covers CommonSocketOptions and its per-type extensions, the eight concrete socket structs, and shared flag/enum types. There is no shared cross-socket-type base trait — each socket type is a standalone struct with its own inherent impl block declaring bind/connect/unbind/disconnect/TLS methods independently; the four PUB/SUB/XPUB/XSUB types share their common surface through an internal macro (impl_pubsub_common!) rather than a public trait. Every socket's send/publish/request/reply returns the operation-builder family documented in the Messaging category. The exact signatures are owned by contracts/sockets/.


Shared socket surface (no base trait)

Options. Every concrete socket type below independently declares this same method set. Every member returns a Result, unlike languages where the equivalent lifecycle call has no error path.

Member Meaning
close(&mut self) -> Result<(), CloseError> closes the native socket
bind(&self, addr: &str) -> Result<(), BindError> starts listening on an address
unbind(&self, addr: &str) -> Result<(), ConnectError> stops listening on an address
last_endpoint(&self) -> Result<String, ConfigError> the concrete resolved bind address
set_tls_cert(&self, cert: &str) / set_tls_key(&self, key: &str) / set_tls_ca(&self, ca_cert: &str) / set_tls_hostname(&self, hostname: &str) / set_tls_trust_system(&self, bool) individual TLS setters — exist here as public methods, unlike every other language covered so far, which only exposes the combined form
set_tls_server(&self, cert, key, require_client_cert: bool) combined server-side TLS setup, apply before bind
set_tls_client(&self, ca_cert, hostname, trust_system: bool) combined client-side TLS setup, apply before connect
connect(&self, addr: &str) / disconnect(&self, addr: &str) connects/disconnects to a peer address; declared on every socket type except StreamSocket (see below)
disconnect_rid(&self, peer_rid: &RoutingId) disconnects the peer identified by that routing id; same exception as above

Completion result. Every one of these methods returns a Result, unlike languages where the equivalent lifecycle call has no error path.

When to use. Call the individual set_tls_cert/set_tls_key/etc. setters when TLS configuration needs to be assembled incrementally (for example, values arriving from separate configuration sources); use the combined set_tls_server/set_tls_client for the common one-call case.


CommonSocketOptions

The typed options facade shared by every socket type, reached via socket.common_options().

let options = socket.common_options();
options.set_send_high_water_mark(100_000)?;
options.set_linger(Duration::from_secs(1))?;
options.set_submit_retry_mode(SubmitRetryMode::LocalFailure)?;

Options. Every getter/setter below returns Result<T, ConfigError>.

Member Meaning
linger() / set_linger(Duration) upper bound on how long close() waits for pending sends to flush
send_high_water_mark() / set_send_high_water_mark(u64) outbound accounted-byte HWM; 0 means unlimited
receive_high_water_mark() / set_receive_high_water_mark(u64) inbound accounted-byte HWM; 0 means unlimited
send_timeout() / set_send_timeout(Duration) upper bound on how long a blocking send waits
receive_timeout() / set_receive_timeout(Duration) upper bound on how long a blocking receive waits
immediate() / set_immediate(bool) whether a send requires a live connection now, instead of queueing until one exists
rid_duplicate_policy() / set_rid_duplicate_policy(RidDuplicatePolicy) what happens when a peer reuses an existing routing id
connect_timeout() / set_connect_timeout(Duration) upper bound on how long connect handshake waits
ipv6() / set_ipv6(bool) whether the socket accepts IPv6 connections
tcp_no_delay() / set_tcp_no_delay(bool) disables Nagle's algorithm when true
tcp_keepalive() / set_tcp_keepalive(bool) OS TCP keepalive mode — a plain on/off bool here, unlike other languages' tri-state -1/0/1 integer
max_message_size() / set_max_message_size(i64) maximum size in bytes of a single accepted message
backlog() / set_backlog(i32) pending-connection queue length for a listening socket
reconnect_interval() / set_reconnect_interval(Duration) delay between reconnect attempts
reconnect_interval_max() / set_reconnect_interval_max(Duration) cap on the reconnect delay
submit_retry_mode() / set_submit_retry_mode(SubmitRetryMode) whether a failed submit retries automatically on local back-pressure
submit_retry_timeout() / set_submit_retry_timeout(Duration) retry timeout when submit_retry_mode() is LocalFailure
submit_retry_attempts() / set_submit_retry_attempts(i32) retry attempt cap when submit_retry_mode() is LocalFailure

Completion result. Every getter/setter is synchronous, returning Result<_, ConfigError>.

When to use. Set send_high_water_mark/receive_high_water_mark and linger before the socket starts exchanging messages when the defaults don't fit the deployment.


PairSocket

An exclusive one-to-one peering socket with no routing.

let pair = ctx.pair_socket()?;
pair.send().message(Message::try_from("ping")?).submit_sync()?;
let mut received = Received::empty();
if pair.recv(&mut received, RecvFlags::NONE)? { /* ... */ }

Options. Plus the shared lifecycle/TLS surface above.

Member Meaning
send(&self) -> SendOp<Empty> starts the shared SendOp builder; choose its Future submit() or blocking submit_sync() terminal
recv(&self, out: &mut Received, flags: RecvFlags) -> Result<bool, RecvError> populates out with the next message
common_options() -> CommonSocketOptions<'_> the shared options facade

Completion result. recv returns Ok(false) only when RecvFlags::DONT_WAIT is set and no message is available.

When to use. Use PAIR for an exclusive point-to-point link — it has no peer routing and does not load-balance.


DealerSocket

Load-balances sends across its connected peers and can issue routed requests.

let dealer = ctx.dealer_socket()?;
dealer.set_routing_id(&"worker-3".into())?;
dealer.request()
    .message(Message::try_from("payload")?)
    .submit_sync()?;

Options. Same shared surface as PairSocket, plus:

Member Meaning
request(&self) -> RequestOp<Empty> starts the shared RequestOp builder; no target parameter — DEALER has no API-level peer routing id
set_routing_id(&self, id: &RoutingId) -> Result<(), ConfigError> / routing_id(&self) -> Result<RoutingId, ConfigError> assigns/reads this socket's own routing id, observed by peers on connect
dealer_options() -> DealerSocketOptions<'_> returns the per-type options facade: set_probe(bool) — set-only, no getter; weight()/set_weight(u32); set_request_timeout(Duration) — set-only, no getter, matching every other language's Dealer asymmetry on this option

Completion result. recv (same shape as PairSocket) follows the same convention.

When to use. Set set_routing_id before connecting so peers observe it from the first message. DEALER has no protocol envelope helper to reply to an arbitrary token — reply from a received request context (Received::reply()) or an explicit ROUTER reply surface instead.


RouterSocket

Routes messages to peers addressed by routing id, and can reply to a specific peer's request.

let router = ctx.router_socket()?;
router.send(&peer_rid).message(Message::try_from("hello")?).submit_sync()?;

Options. Same shared lifecycle/TLS surface as PairSocket (it declares its own copy independently, same as every other socket type here), plus:

Member Meaning
send(&self, target: &RoutingId) -> SendOp<Empty> starts the shared SendOp, addressed to that peer; choose its Future or blocking terminal
recv(&self, out: &mut Received, flags: RecvFlags) -> Result<bool, RecvError> populates out with the next message
request(&self, peer_rid: &RoutingId) -> RequestOp<Empty> Messaging category's RequestOp, addressed to a specific peer
reply(&self, rid: &RoutingId, reply_token: ReplyToken) -> ReplyOp<Empty> Messaging category's ReplyOp, consuming the opaque capability received from this ROUTER
set_routing_id(&self, &RoutingId) / routing_id(&self) assigns/reads this socket's own routing id, observed by peers on connect
common_options() the shared options facade
router_options() -> RouterSocketOptions<'_> returns the per-type options facade: set_mandatory(bool) — set-only; set_probe(bool) — set-only; set_connect_routing_id(&RoutingId) — set-only (no getter for the assigned connect routing id in this binding, unlike dotnet's/cpp's read-only ConnectRoutingId/connect_routing_id() property); weight()/set_weight(u32); request_timeout()/set_request_timeout(Duration) — both directions, unlike Dealer's

Completion result. recv follows the same convention as PairSocket. Managed send/request completion is returned by the chosen terminal; the selected application and completion pipes stay paired inside Core rather than appearing as a separate public channel.

When to use. Use request(peer_rid)/reply(rid, reply_token) for ROUTER-initiated or ROUTER-answered request/reply. Keep the token opaque, socket-owned, and one-shot.


PubSocket / SubSocket / XPubSocket / XSubSocket

PUB publishes topic-filtered messages, dropping ones with no matching subscriber; SUB subscribes with subscriptions set as socket options; XPUB/XSUB add subscriber-event surfacing and message-carried subscriptions respectively. All four share their lifecycle/TLS surface via the internal impl_pubsub_common! macro rather than a public trait.

let pub_socket = ctx.pub_socket()?;
pub_socket.publish("prices").message(Message::try_from(tick)?).submit()?;

let sub = ctx.sub_socket()?;
sub.set_subscription("prices.")?;
let mut msg = TopicMessage::empty();
if sub.subscribe(&mut msg, RecvFlags::NONE)? { /* ... */ }

Options.

Type Member Meaning
PubSocket publish(&self, topic: &str) -> PublishOp<Empty> starts the PublishOp builder; its submit() is synchronous (Result<(), SubmitError>) because PUB is lossy and never waits at a HWM; panics if topic fails an internal fixed-size check (via fixed_topic_or_panic)
common_options() the shared options facade
pub_options() -> PubSocketOptions<'_> the per-type options facade
SubSocket / XSubSocket subscribe(&self, out: &mut TopicMessage, flags: RecvFlags) -> Result<bool, RecvError> populates out with the next matching publish
set_subscription(&self, filter: &str) / unset_subscription(&self, filter: &str) adds/removes a topic filter; subscriptions accumulate
subscription_at(&self, index: usize) -> Result<Option<(String, bool)>, ConfigError> a (filter, is_pattern) tuple rather than a named struct/record — the filter at that index
common_options() the shared options facade
sub_options() -> SubSocketOptions<'_> the per-type options facade
XPubSocket receive_subscription_event(&self, out: &mut SubscriptionEvent, flags: RecvFlags) -> Result<bool, RecvError> adds this on top of the PubSocket shape, in its own impl block (not an extension of PubSocket's type); populates out with the next subscribe/unsubscribe

Neither PubSocket nor XPubSocket declares set_routing_id/routing_id (no routing-id surface at all on either type in this binding). XSubSocket's impl block is a full, independent copy of SubSocket's — same method set, same signatures, no shared type linking the two beyond the identical shape.

Completion result. subscribe/receive_subscription_event follow the Ok(false)-on-DONT_WAIT convention above.

When to use. Use XPubSocket specifically to observe subscriber churn via receive_subscription_event, or manual admission via PubSocketOptions::set_manual/ approve_subscribe/reject_subscribe. Use XSubSocket specifically when subscriptions must be carried as ordinary messages instead — the choice is entirely about which constructor you call (ctx.sub_socket() vs. ctx.xsub_socket()), since the two types' method sets are identical.


StreamSocket

Exchanges framed packets directly with raw TCP peers, outside the zlink wire protocol used by every other socket type.

let stream = ctx.stream_socket()?;
stream.stream_options().set_recv_mode(StreamRecvMode::Packet)?;
let mut packet = StreamPacket::empty();
if stream.recv_packet(&mut packet, RecvFlags::NONE)? { /* use packet */ }
packet.close()?;

Options.

Member Meaning
send(&self, target: &RoutingId) -> SendOp<Empty> starts the shared managed SendOp, addressed to that peer
recv(&self, out: &mut Received, flags: RecvFlags) -> Result<bool, RecvError> pulls the next RAW-mode record and captures its source route for out.send()
recv_packet(&self, out: &mut StreamPacket, flags: RecvFlags) -> Result<bool, RecvError> pulls one PACKET-mode header/body pair into reusable caller-owned storage
disconnect_rid(&self, peer_rid: &RoutingId) -> Result<(), ConnectError> declared directly, since StreamSocket has no connect/disconnect at all — it never declares the connect/disconnect/disconnect_rid trio the other socket types share
set_routing_id(&self, &RoutingId) / routing_id(&self) assigns/reads this socket's own routing id, observed by peers on connect
common_options() the shared options facade
stream_options() -> StreamSocketOptions<'_> the per-type options facade: set_notify(bool)/notify() — delivers peer connect/disconnect as application messages when enabled

Completion result. recv and recv_packet follow the Ok(false)-on-DONT_WAIT convention. The caller owns StreamPacket and releases its messages with close() or drop.

When to use. Set stream_options().set_recv_mode(StreamRecvMode::Raw/Packet) before the first successful bind/connect; changing it afterward returns invalid state. Drain the selected pull API.


Shared flags and enums

Type Used by Values
SendFlags (tuple struct wrapping u32) PublishOp::flags(...) only NONE, DONT_WAIT (associated consts, not enum variants)
RecvFlags (tuple struct wrapping u32) Every recv/subscribe/receive_subscription_event NONE, DONT_WAIT (associated consts)
RidDuplicatePolicy CommonSocketOptions::rid_duplicate_policy Reject, Handover
SubmitRetryMode CommonSocketOptions::submit_retry_mode Off, LocalFailure

When to use. SendFlags is limited to publish; RecvFlags is a struct with pub const NONE/DONT_WAIT associated constants and a bits() accessor, not enum types — a design choice distinct from every other language's flag representation covered so far (dotnet/java's [Flags] enum, cpp's class with static members, node's frozen object constants).


See contracts/sockets/ and the Rust binding spec for the full rationale.