CommsLIBLite is a sockets communication library specifically tailored for IoT and low resource CPUs. It uses low allocations/high performance while providing built-in serialization, framewrapping, autoreconnection and more.
- Low allocation / High performace
- Sockets
- TCP and UDP client sockets
- Autoreconnection if peer down
- Minimum configurable send gap
- Events:
- Connection
- Data rate
- Data available
- TCP Server
- FrameWrapping and Serialization
- Sync and Async serializer/deserializer base clases
- Base classes ready to be inherited to create custom framewrappers/serializers
- Built-in
MessagePack
andprotobuf-net
serializers ready to use
This library has two types of objects, ICommunicator
(the communication object) and FrameWrapperBase<T>
(the serializer/framewrapper).
An ICommunicator
can be used with or without a Serializer/FrameWrapper. Just pass null and you are done.
- Create a Serializer/FrameWrapper if needed. We use here the builtin protobuf one with a serialization message called
MessageBase
.FrameWrapperBase<MessageBase> frameWrapper = new ProtobufFrameWrapper<MessageBase>(true);
- Create ICommunicator from Factory based on the ConnUri, provided Serializer/FrameWrapper and whether CircularBuffer will be used or not (more on this later)
var connUri = new ConnUri("tcp://127.0.0.1:8080"); ICommunicator comm = CommunicatorFactory.CreateCommunicator<MessageBase>(connUri, frameWrapper, false);
- Initialize ICommunicator
// Init with Autoreconnection, no Inactivity detection and no send gap comm.Init(connUri, true, "MyCommunicator", 0, 0);
- Subscribe to events
// ICommunicator events comm.ConnectionStateEvent += OnConnection; comm.DataRateEvent += OnDataRateEvent; comm.DataReadyEvent += OnRawData; // Serializer/FrameWrapper event (only if using a Serializer/FrameWrapper) frameWrapper.FrameAvailableEvent += OnFrame;
- Start both Serializer/FrameWrapper and ICommunicator
frameWrapper.Start(); comm.Start();
- Send. Both SendSync (blocking) and SendASync(non_blocking fire and forget)
// Sync comm.SendSync(byte[] bytes, int offset, int length); // ASync comm.SendASync(byte[] bytes, int length);
- Create ICommunicator from Factory based on the ConnUri, provided Serializer/FrameWrapper and whether CircularBuffer will be used or not (more on this later)
var connUri = new ConnUri("tcp://127.0.0.1:8080"); ICommunicator comm = CommunicatorFactory.CreateCommunicator<object>(connUri, null, false);
- Initialize ICommunicator
// Init with Autoreconnection, no Inactivity detection and no send gap comm.Init(connUri, true, "MyCommunicator", 0, 0);
- Subscribe to events
// ICommunicator events comm.ConnectionStateEvent += OnConnection; comm.DataRateEvent += OnDataRateEvent; comm.DataReadyEvent += OnRawData;
- Start ICommunicator
comm.Start();
ICommunicator
and FrameWrapperBase
have several paremeters that can be used to fit different situations.
ICommunicator
can send bytes to underlying socket in two flavors, blocking and non-blocking.
For the blocking one, it will just call Socket.Send
and block until bytes are sent.
In the non blocking flavour, sending of bytes is done by an internal Task
that waits for data to be ready. There are two options here that are specified at ICommunicator
creation CommunicatorFactory.CreateCommunicator<T>(ConnUri connUri, FrameWrapperBase<T> frameWrapper, bool circular = false)
-
CircularBuffer uses a custom CircularBuffer with SemaphoreSlim to keep "data frames" until they are consumed/sent by the sending task.
Using this option, calling
ICommunicator.SendAsync
will copy source bytes to the internal CircularBuffer (retaining length info) and signal the sender task. -
BlockingQueue uses a BlockingQueue to store the source byte array and signal the sender task.
It should be noted that CircularBuffer flavour is slower but do not care about the source byte array lifespan. On the other side, BlockingQueue is faster but attention should be paid to the lifespan of the source buffer. CircularBuffer allows the caller to reuse same byte array allowing allocation free code.
It is highly recommended tu use CircularBuffer if working with SendASync
. Using BlockingQueue with SendASync
should only be done if you control the lifetime of your byte array.
If doing SendSync
, this option is ignored.
Init method must be used only once. It configures certain internal parameters. Method signature is:
void Init(ConnUri uri, bool persistent, string ID, int inactivityMS, int sendGAP = 0);
- ConnUri uri is the link information including protocol, peer ip, port etc
- bool persistent indicates whether the ICommunicator will attempt to reconnect if connection is broken. Timeout is hardcoded to 5s
- string ID is the ID that will be included in all events to identify the event source
- int inactivityMS defines the maximum period of time (in ms) without incoming data before declaring disconnection. 0 means do not apply
- int sendGAP defines the minimum time between two Send operations
Communication is usually done by means of Messages. Peers exchange some kind of messages than can be mapped to objects.
ICommunicator
can use a provided Serializer/FrameWrapper to Decode incoming bytes to form a Message and the other way around, it can encode a Message into a byte array.
Every Serializer/FrameWrapper must inherit from FrameWrapperBase<T>
, being T the Message. Message is usually a base class.
The key part is the method AddBytes
which is called with every incoming data.
There are two derived Serializer/FrameWrapper from where to inherit to create custom ones.
SyncFrameWrapper
provides a blockingAddBytes
. So Deserialization is done in the same Thread that receives data from the underlying socketAsyncFrameWrapper
provides a non-blockingAddBytes
by using a Queue and a Task