Skip to content

한국어 | English

Reference index

03. Sockets

This category covers _SocketContract (the shared base Protocol), CommonSocketOptions and its per-type extensions, the eight concrete socket Protocol types, and the send/request/reply operation-builder family (declared here, not in Messaging — see the README). The exact signatures are owned by contracts/sockets/.


_SocketContract shared base (private by convention)

The base Protocol every socket type extends: binding, disposal, options. Named with a leading underscore — Python's convention for "not public API," even though socket.py's module-level __getattr__ is how every concrete socket type is actually reached from this package.

socket.bind("tcp://*:5555")
with socket:
    ...

Options. No unbind, no TLS methods (set_tls_server/set_tls_client/etc.) at all — unlike every other language covered so far, this binding's socket base contract has neither.

Member Meaning
bind(endpoint) starts listening on an address
close() closes the native socket
options property, the typed options facade for this socket type
__enter__ / __exit__ sync context-manager only — no __aenter__/__aexit__ here, unlike the async-and-sync-both pattern every other resource type in this binding follows

Completion result. bind/close are synchronous with no return value.

When to use. Every concrete socket type below extends this Protocol and adds its own connect/disconnect/send/recv surface.


CommonSocketOptions and per-type extensions

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

socket.options.send_high_water_mark = 100_000
socket.options.linger_ms = 1000
socket.options.submit_retry_mode = SubmitRetryMode.LOCAL_FAILURE

Options — CommonSocketOptions. All plain get/set properties.

Member Meaning
linger_ms upper bound on how long close() waits for pending sends to flush
send_high_water_mark / receive_high_water_mark outbound/inbound accounted-byte HWM
send_timeout_ms / receive_timeout_ms upper bound on how long a blocking send/receive waits
immediate whether a send requires a live connection now, instead of queueing until one exists
rid_duplicate_policy what happens when a peer reuses an existing routing id
connect_timeout_ms upper bound on how long connect handshake waits
ipv6 whether the socket accepts IPv6 connections
tcp_no_delay disables Nagle's algorithm when True
tcp_keepalive OS TCP keepalive mode
max_message_size maximum size in bytes of a single accepted message
backlog pending-connection queue length for a listening socket
reconnect_interval_ms / reconnect_interval_max_ms delay between reconnect attempts / cap on that delay
submit_retry_mode whether a failed submit retries automatically on local back-pressure
submit_retry_timeout_ms / submit_retry_attempts retry timeout/attempt cap when submit_retry_mode requests it
heartbeat_interval_ms interval between heartbeat pings on an idle connection — no other language covered so far exposes this property
heartbeat_ttl_ms how long the remote keeps the connection alive without a heartbeat — same exclusivity
heartbeat_timeout_ms how long to wait for a heartbeat reply before treating the connection as dead — same exclusivity

Options — per-type extensions.

Type Member Meaning
DealerSocketOptions probe sends an empty probe on connect
weight load-balancing weight
request_timeout_ms request timeout
RouterSocketOptions mandatory error instead of silent drop on an unknown route
handover convenience wrapper over rid_duplicate_policy
probe sends an empty probe on connect
connect_routing_id the only routing-id-shaped surface anywhere in this binding's socket contracts — see the README's note that no socket type has set_routing_id/get_routing_id of its own
weight / request_timeout_ms both directions
StreamSocketOptions notify delivers peer connect/disconnect as application messages when enabled
PubSocketOptions verbose / verboser deliver every (un)subscribe message, including duplicates
manual / manual_last_value subscriptions require approve_subscribe/reject_subscribe; manual_last_value also replays the last cached message per topic to a newly accepted subscriber
no_drop error instead of silent drop on back-pressure
welcome_message sent automatically to each newly connected subscriber
topics_count read-only, active subscription count
approve_subscribe(routing_id) / reject_subscribe(routing_id) set-only, no getters
SubSocketOptions topics_count read-only — the only per-type option this socket has

Completion result. Every property read/write is synchronous.

When to use. Set send_high_water_mark/receive_high_water_mark and linger_ms before the socket starts exchanging messages when the defaults don't fit the deployment. Use the three heartbeat_* properties to tune ZMTP-level liveness detection independent of the transport's own TCP keep-alive.


PairSocket

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

pair = create_pair_socket(ctx)
pair.send().message(b"ping").submit_sync()
received = create_received()
if pair.recv_into(received):
    ...

Options. Plus the _SocketContract base surface.

Member Meaning
connect(endpoint) / disconnect(endpoint) connects/disconnects to a peer address
disconnect_rid(peer_rid) disconnects the peer identified by that routing id
send() starts the shared SendOp builder
recv_into(received, *, flags=0) populates received with the next message, returns bool

Completion result. recv_into returns False only when 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.

dealer = create_dealer_socket(ctx)
reply = await dealer.request().message(b"payload").submit()

Options. No set_routing_id/get_routing_id at all — see the README's note.

Member Meaning
dealer_options property, returns DealerSocketOptions
connect(endpoint) / disconnect(endpoint) connects/disconnects to a peer address
send() starts the shared SendOp builder
request() starts the shared RequestOp builder; no target parameter — DEALER has no API-level peer routing id
recv_into(received, *, flags=0) populates received with the next message

Completion result. recv_into follows the same False-on-DONT_WAIT convention as PairSocket.

When to use. DEALER has no protocol envelope helper to reply to an arbitrary token — reply from a received request context (Received.reply(), Messaging category) 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.

router = create_router_socket(ctx)
await router.send(peer_rid).message(b"hello").submit()

Options. ROUTER uses the same managed send/request terminals as DEALER. Core keeps the selected application pipe and its paired completion pipe together; the binding exposes terminal results, not a separate completion-control channel.

Member Meaning
router_options property, returns RouterSocketOptions
connect(endpoint) / disconnect(endpoint) connects/disconnects to a peer address
send(routing_id) starts the shared SendOp, addressed to that peer
request(routing_id) Messaging category's shared RequestOp, addressed to a specific peer
reply(routing_id, token) the shared ReplyOp, consuming the opaque ReplyToken received from this ROUTER
recv_into(received, *, flags=0) populates received with the next message

Completion result. recv_into follows the convention above.

When to use. Use request(routing_id)/reply(routing_id, 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.

pub = create_pub_socket(ctx)
pub.publish("prices").message(tick).submit()

sub = create_sub_socket(ctx)
sub.set_subscription("prices.")
msg = create_topic_message()
if sub.subscribe_into(msg):
    ...

Options.

Type Member Meaning
PubSocket pub_options the per-type options facade
connect(endpoint) / disconnect(endpoint) connects/disconnects to a peer address
publish(topic) starts the shared SendOp builder
SubSocket sub_options the per-type options facade
connect(endpoint) / disconnect(endpoint) connects/disconnects to a peer address
set_subscription(topic) / unset_subscription(topic) adds/removes a topic filter; subscriptions accumulate
subscription_at(index) (filter, is_pattern) tuple, or None — the filter at that index
subscribe_into(topic_message, *, flags=0) populates topic_message with the next matching publish
XPubSocket pub_options / connect / disconnect / publish same shape as PubSocket
receive_subscription_event_into(event, *, flags=0) populates event with the next subscribe/unsubscribe
XSubSocket sub_options / connect / disconnect / set_subscription / unset_subscription / subscription_at / subscribe_into entirely independent Protocol declaration with the identical member set to SubSocket — no shared base type links the two beyond the matching shape

Completion result. subscribe_into/receive_subscription_event_into follow the False-on-DONT_WAIT convention above.

When to use. Use XPubSocket specifically to observe subscriber churn via receive_subscription_event_into, or manual admission via PubSocketOptions.manual/ approve_subscribe/reject_subscribe. Use XSubSocket specifically when subscriptions must be carried as ordinary messages instead — the choice is entirely about which factory you call (create_sub_socket vs. create_xsub_socket), since the two Protocols' member sets are identical.


StreamSocket

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

stream = create_stream_socket(ctx)
stream.stream_options.recv_mode = StreamRecvMode.PACKET
packet = StreamPacket()
if stream.recv_packet_into(packet):
    packet.close()

Options. No connect/disconnect declared on this Protocol — matching every other language's STREAM asymmetry.

Member Meaning
stream_options the per-type options facade
send(routing_id) starts the shared SendOp, addressed to that peer
recv_into(received, *, flags=0) pulls the next RAW-mode record
recv_packet_into(packet, *, flags=0) pulls the next PACKET-mode header/body pair into reusable caller-owned storage
disconnect_rid(peer_rid) disconnects the peer identified by that routing id

Completion result. recv_into and recv_packet_into follow the convention above. The caller owns the packet and closes its header/body with packet.close().

When to use. Set stream_options.recv_mode to StreamRecvMode.RAW or .PACKET before the first successful bind/connect; changing it afterward raises invalid state. Drain the selected pull API.


Send / request / reply operation-builder shape

The fluent builder every socket type's send/publish/request/reply entry point above returns to accumulate parts and reach a terminal submit. All builder stages extend the shared _FluentMessageOp base Protocol.

await dealer.send().message(part1).message(part2).submit()

reply = await dealer.request().message(payload).timeout(5.0).submit()

received.reply().message(b"ok").submit()

Options.

Stage Member Meaning
_FluentMessageOp (shared base) message(payload) add one part, starts/continues the chain
messages(*payloads) add several parts in one call — declared directly on the shared base Protocol here, unlike other languages where the multi-part convenience is a separate extension method
SendOp extends _FluentMessageOp submit() / submit_sync() awaitable or blocking terminal
RequestOp extends _FluentMessageOp timeout(timeout) / submit() / submit_sync() adds a reply-wait timeout; returns caller-owned reply messages
PublishOp extends _FluentMessageOp flags(flags) / submit() synchronous lossy/NODROP publication; the only operation family retaining send flags
ReplyOp extends _FluentMessageOp submit() terminal

Completion result. Send submit() is awaitable and submit_sync() blocks for Core's terminal result. Request has matching awaitable/blocking terminals returning reply messages; reply and publish submit() are synchronous. Managed send/request/reply terminals do not accept SendFlags.DONT_WAIT.

When to use. Use messages(*payloads) to add several parts in one call instead of chaining .message(...) per part. Prefer the awaitable terminal in asyncio code and use the synchronous terminal only when the calling thread may block.


Socket enums

Enum Used by Values
SocketType Internal socket-kind identification ANY, PAIR, PUB, SUB, DEALER, ROUTER, XPUB, XSUB, STREAM
SendFlags PublishOp.flags(...) only NONE, DONT_WAIT
RecvFlags Every recv_into/subscribe_into/receive_subscription_event_into NONE, DONT_WAIT
SubmitResult Mirrored by SubmitError (Errors category) OK, BACKPRESSURED, NOT_CONNECTED, NOT_FOUND, TERMINATED, INVALID_HANDLE, INVALID_ARGUMENT, NOT_SUPPORTED, INVALID_STATE, THREAD_VIOLATION, OUT_OF_MEMORY, SEQ_EXHAUSTED, INTERNAL_ERROR, NOT_ADMITTED
RequestResult Mirrored by RequestError (Errors category) OK, TIMED_OUT(101), NOT_FOUND(102), TERMINATED(103), PROTOCOL_ERROR(104), INTERNAL_ERROR(105), REJECTED(106), CONFLICT(107), BUSY(108), NOT_CONNECTED(109), INVALID_ARGUMENT(110), INVALID_STATE(111), NOT_SUPPORTED(112), BACKPRESSURED(113)
RecvResult Mirrored by RecvError (Errors category) OK, NO_DATA(201), BUSY(202), TERMINATED(203), INVALID_HANDLE(204), NOT_SUPPORTED(205), INTERNAL_ERROR(206), BUFFER_TOO_SMALL(207), INVALID_STATE(208) — the fuller 8-value set (matching node's, not dotnet/cpp/java/rust's 6-value set)
HandlerResult retained result family; current public completion/event paths use terminals or pull OK, INVALID_ARGUMENT(301), BUSY(302), NOT_SUPPORTED(303), DEADLOCK(304), INVALID_HANDLE(305), INTERNAL_ERROR(306)
RidDuplicatePolicy CommonSocketOptions.rid_duplicate_policy, RouterSocketOptions.handover REJECT, HANDOVER
SubmitRetryMode CommonSocketOptions.submit_retry_mode OFF, LOCAL_FAILURE

When to use. DONT_WAIT on publish or receive turns a blocking call into a non-blocking one that reports False/back-pressure instead of blocking.


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