mirror of
https://github.com/logos-messaging/nim-ffi.git
synced 2026-06-21 16:59:30 +00:00
The native C ABI only works in-process. This example demonstrates the other half — the CBOR ABI crossing a process (and machine) boundary — since the `ctx` pointer is process-local and cannot travel over the wire. A server links libmy_timer, owns one context, and serves method calls; a client links nothing (it only needs the FfiCbor encoder/ffi_decode_text in my_timer_cbor.h) and speaks the same CBOR over a socket. Both binaries accept `--unix <path>` for two processes on one host and `--tcp <host> <port>` for two machines — the only difference is the socket family, so one client/server pair covers both scenarios. Framing is length-prefixed in network byte order so the endpoints may differ in OS, arch, or endianness. `proto.h` carries the shared framing, the CBOR request builders, and a small CBOR map reader so the client can pull text fields out of a response without a full CBOR library. Verified end-to-end over both AF_UNIX and TCP loopback. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
92 lines
3.3 KiB
Markdown
92 lines
3.3 KiB
Markdown
# IPC example — CBOR over a socket (cross-process / cross-machine)
|
|
|
|
The native ABI in [`../c_bindings`](../c_bindings) is for callers that link the
|
|
library **in the same process**. When the caller lives in a *different process*
|
|
— possibly on a *different machine* — there is no shared address space, so the
|
|
request has to be serialized. That is exactly what the **CBOR ABI**
|
|
(`<name>_cbor`, declared in `my_timer_cbor.h`) is for.
|
|
|
|
This example wires that ABI across a socket:
|
|
|
|
- **`server`** links `libmy_timer`, creates one timer context at startup, and
|
|
serves method calls. It owns the `ctx` pointer — which is meaningful only
|
|
inside its own address space and never travels over the wire.
|
|
- **`client`** does **not** link the library. It only builds CBOR request
|
|
payloads (with the `FfiCbor` encoder bundled in `my_timer_cbor.h`) and parses
|
|
CBOR responses. It could be written in any language with a CBOR codec.
|
|
|
|
```
|
|
client process server process
|
|
┌─────────────┐ method + CBOR req ┌──────────────────────────┐
|
|
│ build CBOR │ ─────────────────────▶ │ my_timer_<m>_cbor(ctx,…) │
|
|
│ parse CBOR │ ◀───────────────────── │ libmy_timer (FFI thread) │
|
|
└─────────────┘ ret + CBOR response └──────────────────────────┘
|
|
```
|
|
|
|
Wire framing (network byte order, so endianness never matters):
|
|
|
|
```
|
|
request: [u32 method_len][method][u32 payload_len][cbor payload]
|
|
response: [i32 ret ][u32 resp_len][cbor/raw response]
|
|
```
|
|
|
|
## Build
|
|
|
|
```sh
|
|
cd examples/timer/ipc
|
|
make # builds libmy_timer + server + client
|
|
```
|
|
|
|
## Scenario A — same machine (two processes, AF_UNIX)
|
|
|
|
A Unix-domain socket is the right transport when both ends are on one host.
|
|
|
|
```sh
|
|
make demo # starts the server, runs the client, cleans up
|
|
```
|
|
|
|
or manually, in two terminals:
|
|
|
|
```sh
|
|
# terminal 1
|
|
./server --unix /tmp/my_timer.sock
|
|
|
|
# terminal 2
|
|
./client --unix /tmp/my_timer.sock
|
|
```
|
|
|
|
Expected client output:
|
|
|
|
```
|
|
[client] version = nim-timer v0.1.0
|
|
[client] echo.echoed= hello over the wire
|
|
[client] echo.timer = ipc-server # proves the server's context state round-tripped
|
|
```
|
|
|
|
## Scenario B — separate machines (AF_INET / TCP)
|
|
|
|
The exact same binaries, over TCP. Run the server on host A and the client on
|
|
host B; only the address changes.
|
|
|
|
```sh
|
|
# host A (the server, e.g. 192.168.1.20)
|
|
./server --tcp 0.0.0.0 9099
|
|
|
|
# host B (the client)
|
|
./client --tcp 192.168.1.20 9099
|
|
```
|
|
|
|
Because the wire is self-describing CBOR with network-byte-order framing, the
|
|
two machines may differ in OS, architecture, or endianness. The client needs
|
|
only `my_timer_cbor.h` (or a CBOR library in its own language) — not the
|
|
compiled timer library.
|
|
|
|
## Notes
|
|
|
|
- Every `{.ffi.}` call is dispatched on the library's FFI thread, so the server
|
|
blocks on a condvar-backed callback for each result before replying.
|
|
- The client demonstrates `version` (empty request → text response) and `echo`
|
|
(nested request → `EchoResponse` map). `proto.h` includes a small CBOR reader
|
|
to pull text fields out of the response map; a real client would use its
|
|
language's CBOR library.
|