The @sofidev/ipc protocol operates on a two-way long-polling TCP request. This document explains how
this protocol works and how must be implemented.
The structure is divided in two key components: the Server and the Client, the server opens a IPC/TCP server
and may close it eventually, while a Client can connect to and disconnect from them, whether @sofidev/ipc uses IPC or
IPC is based on where the server is opened to, typically if a numeric port or an IP is passed, it will use TCP, and
IPC when a path to a socket file is passed instead.
Furthermore each Client has a collection of sockets used to send messages to the servers it's connected to, named
ClientSockets, and each Server has a collection of sockets used to send messages to the clients connected to it,
named ServerSocket.
Assuming the server runs on TCP with the port 8002, the connection would be something like the following:
┌────────┐ ┌────────┐
│ Server │ ClientSocket │ Client │
├────────┤ ←--------------------------- ├────────┤
│ 8002 │ ServerSocket │ 8002 │
└────────┘ ---------------------------→ └────────┘
ClientSocketallows Client to send messages to Server, andServerSocketallows Server to send messages to Client.
However, multiple clients can connect to a server just fine.
┌────────┐ ┌────────┐ ┌────────┐
│ Client │ CS │ Server │ CS │ Client │
├────────┤ ---→ ├────────┤ ←--- ├────────┤
│ 8002 │ SS │ 8002 │ SS │ 8002 │
└────────┘ ←--- └────────┘ ---→ └────────┘
↑ |
CS | | SS
| ↓
┌────────┐
│ Client │
├────────┤
│ 8002 │
└────────┘
CSandSSrefer toClientSocketandServerSocket, respectively.
@sofidev/ipc uses messagepack-encoded messages to communicate between all sockets, to do so, it uses msgpackr
to encode messages before sending them, and all the communication happens in ClientSocket and in ServerSocket
exclusively, since they define a socket connection.
Each socket is a duplex connection, so they have both a message sender and a receiver. The composition of messages
using the @sofidev/ipc protocol is the following:
| ID | Receptive | Byte-Length | Bytes ... |
|---|---|---|---|
| 6 bytes | 1 byte | 4 bytes | Byte-Length bytes |
- Implementation-wise there is no restriction about what
IDmay be, it can be a Cryptographic Nonce, it may be an incremental number, it may be anything, as long as two messages from the same sender do not conflict and is strictly 6 bytes long. Receptiveheader defines whether the message isread-onlyor the server is awaiting its response, will always be0x00for non-receptive, or0x01for receptive.Byte-Lengthis used to define how long the message is in bytes.Bytesis the message encoded with msgpackr.
When a message is receptive, it must be replied with the same ID, this will help the sender identify the original
message and resolve the value (like a HTTP GET would work, where you give the URL and you get the response from the
request).
It is suggested to make a queue receiver to handle the messages, @sofidev/ipc takes advantage of TCP's buffering nature, which
helps lowering the latency and the time it takes to send the messages, which can also mean that two or more messages
may be received glued up; use the Byte-Length header to find the end of the message (and the start of the next), and
it also may be received partial ─ most routers allow up to 65536 bytes in a segment, so when sending large messages (for
example files), it is suggested to implement a buffer control to join all the segments once it is received full.
The connection between the Client and the Server is done over TCP but has an extra step: handshake.
The handshake has two purposes:
- Verify: This step helps identifying if the counterpart "understands" the same language. The slight decode error or type mismatch should end on a prompt disconnection.
- Identify:
@sofidev/ipcnodes have a name for which they are identified as. For example if aServeris namedmaster, allServers connected to it may send messages to it usingmasteras its name.
// Stablish a TCP connection by connecting Client
// to Server.
┌────────┐ ┌────────┐
│ Server │ │ Client │
├────────┤ TCP Connection ├────────┤
│ master │ ←--------------------------→ │ socket │
│ 8002 │ │ 8002 │
└────────┘ └────────┘
// Once the connection has stablished, the server
// will send its name ('master') to the client.
┌────────┐ ┌────────┐
│ Server │ "master" │ Client │
├────────┤ ---------------------------→ ├────────┤
│ master │ │ socket │
│ 8002 │ │ 8002 │
├────────┤ ├────────┤
│ ?????? │ │ ?????? │
└────────┘ └────────┘
// The client must reply with its name ('socket')
// to the server.
┌────────┐ ┌────────┐
│ Server │ │ Client │
├────────┤ ├────────┤
│ master │ │ socket │
│ 8002 │ "socket" │ 8002 │
├────────┤ ←--------------------------- ├────────┤
│ socket │ │ ?????? │
└────────┘ └────────┘
// Now both nodes "know" each others names and the
// connection has successfully verified. Now you
// may send messages from the Server to the Client
// using "socket", or vice versa using "master".
┌────────┐ ┌────────┐
│ Server │ │ Client │
├────────┤ ├────────┤
│ master │ │ socket │
│ 8002 │ │ 8002 │
├────────┤ ├────────┤
│ socket │ │ master │
└────────┘ └────────┘