In a 20 thousand feet view, two persistent communication channels* will be maintained, one for commands generally coming from the ZoomShop (client) to the NCube (server) for dispensing, inventory requests, etc. in an RPC style and another one for events (in fire-and-forget style) which flow is to be started from the NCube (server) to the ZoomShop (client).
Having persistent connections benefits both parties as both can immediately detect when a connection was broken and take the appropriate measures.
On a related note, but not relevant to this document, both commands and events messages (see NCube Protocol Format > Messages) are to be serialized as XML documents.
*Note: Within this document, the term stream, socket connection and channel are treated as synonyms.
A session is said to be initiated or established when both parties (client and server) have negotiated their commands and events channels (see Handshake), have persistent connections to them and are ready for letting commands and events flow back and forward through the associated streams.
In case a client starts sending commands without the session properly initialized, a corresponding protocol error should be returned (see NCube Protocol Format > Errors).
In case the client gets disconnected, the session has to be reset to uninitialized until a new session is correctly established. The server has to be resilient to client disconnections/re-connections.
The handshake is comprised of two subsequent sockets connections with the corresponding Protocol Control initialization command (in no particular order) - (see NCube Protocol Format > Protocol Control commands). For each of them, the server should return a success
Protocol Control response if there were no errors.
The session is said to be initiated only when the two streams ('commands' and 'events') were established and no error was returned in the process.
Once the handshake has taken place and thus the session considered to be initiated, the handshake is considered to be complete and both commands and events can start to flow freely on both directions.
Due to the nature of our application (robot control in some cases), the server can only service one and only one client. In case a new handshake tries to take place under a correctly initialized ongoing session, the server should ignore it (idempotent) and should notify the client the connection is being ignored before closing it (see NCube Protocol Format > Protocol Commands).
The server should immediately become receptive to a new handshake if any of the connections broke and thus session became unstable.
At a low level, the communication between client and server happens in packets of NCube Protocol Formats (NCProto).
The overall format of an NCProto is:
+-------+----+-------------+
|1 |1 |N |
|-------|----|-------------|
|Version|Type|Data |
+-------+----+-------------+
Where:
Version
(1 byte): representing the version of the protocol (currently 1)Type
(1 byte): representing the type.- Possible values:
- 1 for Protocol Control
- 2 for Messages
- 3 for Error
- Possible values:
Data
(N bytes): Whatever data is represented on the NCProto. IfType
specifies a message, then its the message bytes, if theType
is an error, then a 1 byteErr Type
follows with anErr Desc
immediately after (see Errors), etc.
When the Type
is specified as Protocol Control (1) the Data
is just one byte:
+-------+----+-------------+
|1 |1 |1 |
|-------|----|-------------|
|Version|Type|Data |
+-------+----+-------------+
The 1 byte Data
can have any of the following values:
- 0: success - Generally an 'ok' response from the server to any protocol control commands. If there is an error, like unsupported version, an error type should be returned with the according description (see Errors)
- 1: connection request ignored - Returned by the server if there is already an ongoing correctly initialized session.
- 10: initialize commands stream - Generally used at handshake (see handshake)
- 20: initialize events stream - Generally used at handshake (see handshake)
Once the handshake has been done (session established) this types will most likely constitute the bulk of the communication.
The format is as follows
+-------+----+-------------+
|1 |1 |N |
|-------|----|-------------|
|Version|Type|Message Data |
+-------+----+-------------+
where Type
will be 2 and Data
is just a set of UTF-8 encoded bytes that can be reconstructed into a string.
When there is a protocol error, e.g.: the version specified on a request is not supported, or a client is trying to send a message and the session hasn't been initiated (no handshake), or an unsupported type was set, there should be an error returned.
The format of the error follows:
+-------+----+--------+-------------+
|1 |1 |1 |N |
|-------|----|--------|-------------|
|Version|Type|Err Type|Err Desc |
+-------+----+--------+-------------+
Where Err Type
can have any of the following values:
- 30: Uninitialized Session
- 40: Unsupported Version
- 41: Unrecognized Type
- 42: Malformed NCProto - When the NCProto doesn't conform to the spec.
- 50: Server Error
The Err Desc
is optional though encouraged. It should be a UTF-8 encoded string with the description/stack-trace of the error.
To transmit the NCProtos over the wire we use the concept of packets. A packet is an NCProto simply prepended with a four byte integer (big endian) as header representing its length.
For example, a 20 byte NCProto, would have the binary header:
00000000 00000000 00000000 00010100
The header would be followed by 20 bytes of NCProto data, making the packet 24 bytes overall. Reading streams of NCProtos involves reading the four byte header, then reading as many bytes as specified in the length, and starting over again.