Skip to content

한국어 | English

Socket Index | Previous: Socket Overview | Next: PUB

Socket — PAIR

What this chapter defines — The exclusive 1:1 connection behavior and public contract of a PAIR socket.

1. PAIR overview

PAIR is a bidirectional socket type in which two socket instances form an exclusive 1:1 connection and both sides send and receive messages. Because there is exactly one peer—the socket at the other end of the connection—there is no input for selecting a destination peer, and a received record has no source routing ID. PAIR has no type-specific options.

This document defines only the PAIR-specific contract: how the whole-message send and receive functions behave with PAIR, the asynchronous send rules for admission—the decision to accept a send request into the Core send queue—and the absence of receive-flow state. It does not redefine contracts shared by all socket types.

The following documents own the related contracts.

Related contract Defining document
socket creation, common options, send/recv flags, and result enums Socket Common
send ownership, completion reservation bounds, and pull completion Socket Common
message lifecycle, ownership, and multipart Message
result-to-errno mapping Errors

2. Whole-message sends and record atomicity

A PAIR socket passes a parts_ array and part_count_ to one zlink_send() call to submit one record. The part order of a multipart message is the array order. A single-part message uses an array of length one.

Record atomicity, input-slot consumption, and complete-record resubmission after failure follow Socket Common whole-message send.

%%{init: {'sequence': {'actorFontSize': '18px', 'messageFontSize': '18px', 'noteFontSize': '18px', 'boxMargin': 8, 'width': 140}, 'themeVariables': {'fontSize': '18px'}}}%%
sequenceDiagram
    participant App as Application
    participant Core as Core
    App->>Core: zlink_send(parts, part_count, flags)
    alt Record admission succeeds
        Core-->>App: ZLINK_SUBMIT_OK
        Note over Core: Submit the complete record as one unit
    else Record admission fails
        Core-->>App: Submit error
        Note over Core: The peer sees no part of the record
    end

PAIR receive uses zlink_recv to receive a complete record in one call. Because there is exactly one peer, the source routing ID is NULL. Ownership, close, capacity, and record-atomicity rules are owned by Socket Common.

3. Receive flow state

Socket Common defines the receive-flow state and constants that DEALER and ROUTER sockets use to notify a peer to pause or resume receiving.

PAIR is not a socket type that supports receive flow. zlink_socket_set_receive_flow_state() returns ZLINK_CONFIG_NOT_SUPPORTED with errno == ENOTSUP for a PAIR socket and changes nothing. The byte HWM (the byte limit retained by a queue), low water mark, and transport backpressure (restriction on additional submissions by the sender) owned by Socket Common remain in effect. A PAIR socket's monitor does not report receive-flow state — §5 ("Absence of receive flow state") lists the observable detail.

4. Functions

Sends one message record.

ZLINK_EXPORT zlink_submit_result_t zlink_send (
  void *s_, zlink_msg_t *parts_, size_t part_count_,
  zlink_send_flags_t flags_, void *user_context_,
  zlink_completion_id_t *completion_id_out_);

The parts_ array and part_count_ form the record. This function consumes every input slot on both success and failure. Retain a copy of the complete record before the call if it may be needed again. part_count_ == 0 returns ZLINK_SUBMIT_INVALID_ARGUMENT with EINVAL. Pass ZLINK_SEND_FLAGS_NONE or ZLINK_SEND_FLAGS_DONTWAIT in flags_. NONE snapshots SNDTIMEO on entry and waits through local queue admission; DONTWAIT does not wait. Socket Common owns the exact optional ID-output and context rules.

Returns: ZLINK_SUBMIT_OK on success; otherwise a zlink_submit_result_t value that identifies the cause. The full mapping follows the errno map.

See also: zlink_recv, zlink_completion_recv


Receives one message record.

ZLINK_EXPORT zlink_recv_result_t zlink_recv (
  void *s_,
  const zlink_routing_id_t **source_rid_out_,
  zlink_msg_t *parts_out_,
  size_t parts_capacity_,
  size_t *part_count_out_,
  zlink_recv_flags_t flags_);

parts_out_ and part_count_out_ are required, and the array slots need not be initialized. source_rid_out_ is optional and receives NULL on success. On success, ownership of the leading *part_count_out_ slots transfers to the caller, which closes them exactly once with zlink_multipart_close(parts_out_, *part_count_out_).

If parts_capacity_ is smaller than the record's part count, the record is not consumed, the needed count is written to *part_count_out_, and the call returns ZLINK_RECV_BUFFER_TOO_SMALL with ENOBUFS. Retrying with a large enough array receives the same record. A ZLINK_RECV_FLAGS_DONTWAIT call with no available record returns ZLINK_RECV_NO_DATA with EAGAIN.

Returns: ZLINK_RECV_OK on success; otherwise a zlink_recv_result_t value.

See also: zlink_send, zlink_msg_close


PAIR logical route and reconnect

The PAIR SEND target is the single logical route even when its physical pipe changes. Pipe attach after reconnect is a wake edge for that route’s wait token. SEND results, WRITABLE resubmission, token lifetime, and replay prohibition follow Socket Common whole-message send.

5. Implementation and contract-test verification requirements

Verify the following using only the public surface (zlink_send, zlink_recv, zlink_completion_recv, zlink_socket_set_receive_flow_state, monitor observations, return values, and errno). Each item maps to one test.

1:1 send and receive - Both connected PAIR sockets can send with zlink_send and receive with zlink_recv. - When zlink_recv succeeds, a caller that provides source_rid_out_ receives NULL. - After a successful receive, the caller owns the leading *part_count_out_ slots and closes them exactly once with zlink_multipart_close. A failure does not transfer slot ownership. - If parts_capacity_ is smaller than the record's part count, the call returns ZLINK_RECV_BUFFER_TOO_SMALL with ENOBUFS and the needed count without consuming the record; retrying with a large enough array receives the same record.

Whole-message send and ownership - PAIR SEND result, record atomicity, input consumption, and WRITABLE resubmission verification refer to Socket Common whole-message send.

Logical reconnect and completion - If the connection disconnects while a wait token is live and the same PAIR logical route reconnects, the pipe attach publishes the WRITABLE record for that token, and the disconnect alone does not produce a TERMINAL record. - A NONE send waits for reconnect of the same logical route within the snapshotted SNDTIMEO; expiration returns ZLINK_SUBMIT_BACKPRESSURED with EAGAIN, ID 0, and no completion. - Disconnecting and reconnecting after ID 0 does not replay the same application record; the retransmission after a WRITABLE record is a record the application submitted again. - Removing the endpoint with zlink_disconnect() ends the token with a WRITABLE record carrying ZLINK_SEND_NOT_FOUND and ENOENT. After socket close no record for that token can be received — close ends the token internally and delivers no record.

Absence of receive flow state - zlink_socket_set_receive_flow_state() returns ZLINK_CONFIG_NOT_SUPPORTED with errno == ENOTSUP for a PAIR socket, while byte HWM, low water mark, and transport backpressure behavior remain in effect. - A PAIR socket's monitor status does not set ZLINK_MONITOR_STATUS_DETAIL_FLOW_STATE. - A PAIR socket does not emit ZLINK_EVENT_SEND_FLOW_PAUSED, ZLINK_EVENT_SEND_FLOW_RESUMED, or ZLINK_EVENT_FLOW_STATE_STALE events.

Socket Common owns verification of ownership transfer, completion reservation bounds, close, and pull completion.

Socket Index | Previous: Socket Overview | Next: PUB