콘텐츠로 이동

한국어 | English

레퍼런스 목차

03. Sockets

이 category는 CommonSocketOptions와 타입별 확장, 8개 구체 socket struct, 공유 flag/enum 타입을 다룬다. socket type 전체를 아우르는 공유 기반 trait이 없다 — 각 socket type은 bind/connect/unbind/ disconnect/TLS 메서드를 독립적으로 선언하는 자신만의 inherent impl block을 가진 독립 struct다. PUB/SUB/XPUB/XSUB 4개 타입은 public trait이 아니라 내부 macro(impl_pubsub_common!)를 통해 공통 표면을 공유한다. 모든 socket의 send/publish/request/reply는 Messaging category에 문서화된 operation-builder family를 반환한다. 정확한 signature는 contracts/sockets/가 소유한다.


공유 socket 표면(공유 기반 trait 없음)

Options. 아래 모든 구체 socket type은 같은 메서드 집합을 독립적으로 선언한다. 이 메서드 전부가 Result를 반환한다, 대응하는 lifecycle 호출에 error 경로가 없는 다른 언어와 다르다.

Member 의미
close(&mut self) -> Result<(), CloseError> native socket을 닫는다
bind(&self, addr: &str) -> Result<(), BindError> 주소에서 listen을 시작
unbind(&self, addr: &str) -> Result<(), ConnectError> 주소에서 listen을 중단
last_endpoint(&self) -> Result<String, ConfigError> 실제로 해석된 bind 주소
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) 개별 TLS setter — 여기 public 메서드로 존재한다, 지금까지 다룬 다른 모든 언어가 결합된 형태만 노출하는 것과 다르다
set_tls_server(&self, cert, key, require_client_cert: bool) 결합된 서버측 TLS 설정, bind 전에 적용
set_tls_client(&self, ca_cert, hostname, trust_system: bool) 결합된 클라이언트측 TLS 설정, connect 전에 적용
connect(&self, addr: &str) / disconnect(&self, addr: &str) peer 주소로 connect/disconnect; StreamSocket(아래 참고)을 제외한 모든 socket type에 선언됨
disconnect_rid(&self, peer_rid: &RoutingId) 해당 routing id로 식별되는 peer를 disconnect; 위와 같은 예외

Completion result. 이 메서드 전부가 Result를 반환한다, 대응하는 lifecycle 호출에 error 경로가 없는 다른 언어와 다르다.

선택 기준. TLS 설정을 점진적으로 조립해야 할 때(예: 서로 다른 설정 소스에서 값이 도착할 때) 개별 set_tls_cert/set_tls_key 등 setter를 호출한다. 일반적인 한 번의 호출로 끝나는 경우엔 결합된 set_tls_server/set_tls_client를 쓴다.


CommonSocketOptions

socket.common_options()로 도달하는, 모든 socket type이 공유하는 typed option facade.

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. 아래 모든 getter/setter는 Result<T, ConfigError>를 반환한다.

Member 의미
linger() / set_linger(Duration) close()가 대기 중인 send를 flush하기 위해 기다리는 상한
send_high_water_mark() / set_send_high_water_mark(u64) outbound accounted-byte HWM; 0은 무제한
receive_high_water_mark() / set_receive_high_water_mark(u64) inbound accounted-byte HWM; 0은 무제한
send_timeout() / set_send_timeout(Duration) blocking send가 기다리는 상한
receive_timeout() / set_receive_timeout(Duration) blocking receive가 기다리는 상한
immediate() / set_immediate(bool) send가 지금 당장 살아있는 연결을 요구하는지, 아니면 연결이 생길 때까지 큐잉하는지
rid_duplicate_policy() / set_rid_duplicate_policy(RidDuplicatePolicy) peer가 기존 routing id를 재사용할 때 벌어지는 일
connect_timeout() / set_connect_timeout(Duration) connect handshake가 기다리는 상한
ipv6() / set_ipv6(bool) socket이 IPv6 연결을 받아들이는지
tcp_no_delay() / set_tcp_no_delay(bool) true면 Nagle 알고리즘을 비활성화
tcp_keepalive() / set_tcp_keepalive(bool) OS TCP keepalive 모드 — 여기선 순수 on/off bool이다, 다른 언어의 tri-state -1/0/1 정수와 다름
max_message_size() / set_max_message_size(i64) 수신 허용하는 메시지 한 건의 최대 바이트 크기
backlog() / set_backlog(i32) listening socket의 대기 연결 큐 길이
reconnect_interval() / set_reconnect_interval(Duration) 재연결 시도 사이 간격
reconnect_interval_max() / set_reconnect_interval_max(Duration) 재연결 간격의 상한
submit_retry_mode() / set_submit_retry_mode(SubmitRetryMode) local back-pressure에서 실패한 submit이 자동 재시도되는지
submit_retry_timeout() / set_submit_retry_timeout(Duration) submit_retry_mode()LocalFailure일 때의 재시도 timeout
submit_retry_attempts() / set_submit_retry_attempts(i32) submit_retry_mode()LocalFailure일 때의 재시도 횟수 상한

Completion result. 모든 getter/setter는 동기이며 Result<_, ConfigError>를 반환한다.

선택 기준. 기본값이 배포 환경에 맞지 않을 때 socket이 메시지 교환을 시작하기 전에 send_high_water_mark/receive_high_water_mark, linger를 설정한다.


PairSocket

라우팅이 없는 배타적 1:1 peering socket.

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. 위 공유 lifecycle/TLS 표면에 더해:

Member 의미
send(&self) -> SendOp<Empty> 공유 SendOp builder를 시작; Future submit() 또는 blocking submit_sync() terminal 선택
recv(&self, out: &mut Received, flags: RecvFlags) -> Result<bool, RecvError> out을 다음 메시지로 채움
common_options() -> CommonSocketOptions<'_> 공유 option facade

Completion result. recvRecvFlags::DONT_WAIT가 설정되고 메시지가 없을 때만 Ok(false)를 반환한다.

선택 기준. 배타적 point-to-point 링크엔 PAIR를 쓴다 — peer 라우팅이 없고 load-balance하지 않는다.


DealerSocket

연결된 peer 전체에 send를 load-balance하고 routed request를 낼 수 있다.

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

Options. PairSocket과 같은 공유 표면에 더해:

Member 의미
request(&self) -> RequestOp<Empty> 공유 RequestOp builder를 시작; target 인자 없음 — DEALER는 API 레벨 peer routing id가 없기 때문
set_routing_id(&self, id: &RoutingId) -> Result<(), ConfigError> / routing_id(&self) -> Result<RoutingId, ConfigError> 이 socket 자신의 routing id를 지정/조회, peer가 connect 시 관찰
dealer_options() -> DealerSocketOptions<'_> 타입별 option facade 반환: set_probe(bool) — set-only, getter 없음; weight()/set_weight(u32); set_request_timeout(Duration) — set-only, getter 없음, 이 option에 대한 다른 모든 언어의 Dealer 비대칭과 일치

Completion result. recv(PairSocket과 같은 형태)는 같은 관례를 따른다.

선택 기준. peer가 첫 메시지부터 이를 관찰하도록 connect 전에 set_routing_id를 설정한다. DEALER는 임의 token에 reply할 protocol envelope helper가 없다 — 대신 수신된 request context (Received::reply())나 명시적 ROUTER reply 표면에서 답한다.


RouterSocket

routing id로 지정된 peer에게 메시지를 보내고, 특정 peer의 request에 reply할 수 있다.

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

Options. PairSocket과 같은 공유 lifecycle/TLS 표면(여기서도 자신만의 사본을 독립적으로 선언한다, 다른 모든 socket type과 동일)에 더해:

Member 의미
send(&self, target: &RoutingId) -> SendOp<Empty> 공유 SendOp을 그 peer로 향해 시작; Future 또는 blocking terminal 선택
recv(&self, out: &mut Received, flags: RecvFlags) -> Result<bool, RecvError> out을 다음 메시지로 채움
request(&self, peer_rid: &RoutingId) -> RequestOp<Empty> Messaging category의 RequestOp, 특정 peer로 향함
reply(&self, rid: &RoutingId, reply_token: ReplyToken) -> ReplyOp<Empty> Messaging category의 ReplyOp, 이 ROUTER가 받은 opaque capability를 소비
set_routing_id(&self, &RoutingId) / routing_id(&self) 이 socket 자신의 routing id를 지정/조회, peer가 connect 시 관찰
common_options() 공유 option facade
router_options() -> RouterSocketOptions<'_> 타입별 option facade 반환: set_mandatory(bool) — set-only; set_probe(bool) — set-only; set_connect_routing_id(&RoutingId) — set-only(이 binding엔 할당된 connect routing id의 getter가 없다, dotnet/cpp의 읽기 전용 ConnectRoutingId/connect_routing_id() property와 다름); weight()/set_weight(u32); request_timeout()/set_request_timeout(Duration) — Dealer와 달리 양방향 모두

Completion result. recvPairSocket과 같은 관례를 따른다. Managed send/request completion은 선택한 terminal이 반환한다. 선택된 application/completion pipe는 Core 안에서 paired 상태를 유지하며 별도 public channel로 노출되지 않는다.

선택 기준. DEALER가 특정 peer를 지정할 수 없는 ROUTER 주도·ROUTER 응답 request/reply엔 request(peer_rid)/reply(rid, reply_token)을 쓴다. Token은 opaque하고 socket-owned이며 one-shot이다.


PubSocket / SubSocket / XPubSocket / XSubSocket

PUB는 매칭되는 구독자가 없으면 버리는 topic-filtered 메시지를 publish하고, SUB는 구독을 socket option으로 설정하는 방식으로 구독하며, XPUB/XSUB는 각각 구독자-event 노출과 메시지로 실어 나르는 구독을 더한다. 넷 다 public trait이 아니라 내부 impl_pubsub_common! macro를 통해 lifecycle/TLS 표면을 공유한다.

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.

타입 Member 의미
PubSocket publish(&self, topic: &str) -> PublishOp<Empty> PublishOp builder를 시작; PUB는 lossy이고 HWM에서 대기하지 않으므로 submit()은 동기 Result<(), SubmitError>; topic이 내부 고정 크기 검사를 통과하지 못하면 panic(fixed_topic_or_panic을 통해)
common_options() 공유 option facade
pub_options() -> PubSocketOptions<'_> 타입별 option facade
SubSocket / XSubSocket subscribe(&self, out: &mut TopicMessage, flags: RecvFlags) -> Result<bool, RecvError> out을 다음 매칭 publish로 채움
set_subscription(&self, filter: &str) / unset_subscription(&self, filter: &str) topic filter를 추가/제거; 구독은 누적된다
subscription_at(&self, index: usize) -> Result<Option<(String, bool)>, ConfigError> 명명된 struct/record가 아니라 (filter, is_pattern) tuple — 해당 index의 filter
common_options() 공유 option facade
sub_options() -> SubSocketOptions<'_> 타입별 option facade
XPubSocket receive_subscription_event(&self, out: &mut SubscriptionEvent, flags: RecvFlags) -> Result<bool, RecvError> PubSocket 형태 위에 이걸 더함, 자신만의 impl block(PubSocket 타입의 확장이 아님); out을 다음 subscribe/unsubscribe로 채움

PubSocketXPubSocketset_routing_id/routing_id를 선언하지 않는다(이 binding의 두 타입 모두에 routing-id 표면이 전혀 없다). XSubSocketimpl block은 SubSocket의 완전하고 독립적인 사본이다 — 같은 method 집합, 같은 signature, 동일한 형태 외엔 둘을 연결하는 공유 타입이 없다.

Completion result. subscribe/receive_subscription_event는 위 DONT_WAIT에서 Ok(false) 관례를 따른다.

선택 기준. receive_subscription_event로 구독자 변동을 관찰하거나 PubSocketOptions::set_manual/approve_subscribe/reject_subscribe로 수동 admission을 하려면 특별히 XPubSocket을 쓴다. 구독을 일반 메시지로 실어 날라야 할 때만 특별히 XSubSocket을 쓴다 — 두 타입의 method 집합이 동일하므로 선택은 전적으로 어떤 생성자를 호출하는지 (ctx.sub_socket() vs ctx.xsub_socket())에 달려 있다.


StreamSocket

다른 모든 socket type이 쓰는 zlink wire protocol 밖에서, raw TCP peer와 framed packet을 직접 주고받는다.

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)? { /* packet 사용 */ }
packet.close()?;

Options.

Member 의미
send(&self, target: &RoutingId) -> SendOp<Empty> 공유 managed SendOp을 그 peer로 향해 시작
recv(&self, out: &mut Received, flags: RecvFlags) -> Result<bool, RecvError> 다음 RAW-mode record를 pull하고 out.send()용 source route를 포착
recv_packet(&self, out: &mut StreamPacket, flags: RecvFlags) -> Result<bool, RecvError> PACKET-mode header/body 하나를 재사용 caller-owned storage로 pull
disconnect_rid(&self, peer_rid: &RoutingId) -> Result<(), ConnectError> 직접 선언됨, StreamSocketconnect/disconnect가 전혀 없다 — 다른 socket type이 공유하는 connect/disconnect/disconnect_rid 삼총사를 아예 선언하지 않는다
set_routing_id(&self, &RoutingId) / routing_id(&self) 이 socket 자신의 routing id를 지정/조회, peer가 connect 시 관찰
common_options() 공유 option facade
stream_options() -> StreamSocketOptions<'_> 타입별 option facade: set_notify(bool)/notify() — 활성화 시 peer connect/disconnect를 application 메시지로 전달

Completion result. recvrecv_packet은 위 DONT_WAIT에서 Ok(false) 관례를 따른다. Caller가 StreamPacket을 소유하며 close() 또는 drop으로 message를 해제한다.

선택 기준. 첫 successful bind/connect 전에 stream_options().set_recv_mode(StreamRecvMode::Raw/Packet)을 정한다. 이후 변경은 invalid state며, 선택한 pull API를 drain한다.


공유 flag와 enum

타입 사용처
SendFlags(u32를 감싸는 tuple struct) PublishOp::flags(...)에만 사용 NONE, DONT_WAIT(enum variant가 아니라 associated const)
RecvFlags(u32를 감싸는 tuple struct) 모든 recv/subscribe/receive_subscription_event NONE, DONT_WAIT(associated const)
RidDuplicatePolicy CommonSocketOptions::rid_duplicate_policy Reject, Handover
SubmitRetryMode CommonSocketOptions::submit_retry_mode Off, LocalFailure

선택 기준. SendFlags는 publish에 제한된다. RecvFlagsenum 타입이 아니라 pub const NONE/DONT_WAIT associated constant와 bits() accessor를 가진 struct다 — 지금까지 다룬 다른 모든 언어의 flag 표현(dotnet/java의 [Flags] enum, cpp의 static member를 가진 class, node의 freeze된 객체 상수)과 구별되는 설계 선택이다.


contracts/sockets/Rust 바인딩 스펙에서 전체 근거를 확인한다.