Dual-homed UDP link helper (ADR-029) - keeps one socket open to EACH
site's C at all times, reconnecting independently, so a Train/IL sim
never visibly drops contact across a site failover: whichever site is
currently ONLINE is the only one that ever answers, but both sockets stay
open so there is nothing to notice or reconnect from the sim's own point
of view when ONLINE moves from one site to the other. See ADR-029's own
"Failover continuity" design note.
Transport: UDP with a HELLO/HELLO-ACK handshake (ADR-027), NOT plain TCP -
this project's own sapi_netlink POSIX backend
(safeAPIRBC2oo2/src/posix_backend/sapi_posix_backend_netlink.c) moved off
TCP project-wide; C's LISTEN side for these links speaks the exact same
protocol it already does for A/B. This module is a hand-ported Python
mirror of that CONNECT-side handshake (open_connect_udp() in that C
file) - keep the two in sync if that protocol ever changes:
- HELLO = 0xA5, HELLO_ACK = 0x5A, one byte each.
- CONNECT side: connect() a UDP socket (fixes the default peer address
only - no network I/O, no reachability signal by itself), then send
HELLO every ~20ms and poll for a 1-byte 0x5A reply until one arrives
or a bounded budget elapses.
Once handshake completes, every later send()/recv() on that socket is
exactly one RBC envelope datagram - sapi_netlink's own message-per-
datagram framing, matching RBC_ENVELOPE_WIRE_SIZE-sized reads.
First simple pass, not a general-purpose framing layer: each recv() is
expected to be exactly one whole datagram (true for UDP, unlike TCP's own
stream semantics) of exactly message_size bytes; a differently-sized
datagram is dropped (logged), not reassembled or truncated-and-kept.