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 open
s 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
ClientSocket
s, 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 │
└────────┘ ---------------------------→ └────────┘
ClientSocket
allows Client to send messages to Server, andServerSocket
allows 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 │
└────────┘
CS
andSS
refer toClientSocket
andServerSocket
, 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
ID
may 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. Receptive
header defines whether the message isread-only
or the server is awaiting its response, will always be0x00
for non-receptive, or0x01
for receptive.Byte-Length
is used to define how long the message is in bytes.Bytes
is 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/ipc
nodes have a name for which they are identified as. For example if aServer
is namedmaster
, allServer
s connected to it may send messages to it usingmaster
as 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 │
└────────┘ └────────┘