Corelink CPP Client
Corelink C++ client library
 
Loading...
Searching...
No Matches
What is Corelink?

Notes

‍These documents are a work in progress, and we are trying to improve upon them on a daily basis. However, given our capacity and workload, there are bound to be things which get missed or checked in which should not have been checked in. If you find mistakes or documents wanting information, and you would like to help, you can do so in two ways

  1. Submit corrections. When you update anything and submit a PR, we will review it and if all looks good, it will be added. This is the fastest way that one can expect changes to be in.
  2. Submit tickets. If you are not sure how to make updates, or if you are short on time, please raise an issue in the repo, with details and what you think needs to be the expected text. We will try and update the literature to reflect any changes that we feel are merited. Note that this route will take longer than the first approach and there are no guarantees on the timeline.

Corelink C++ Client

The Corelink C++ client is the official client library for using the Corelink low-latency messaging platform with C++.

The client functions as the interface layer between the applications and the server, and is where you create sender and receiver streams which communicate with the server and subsequently with other clients.

Corelink C++ utilizes std::async to perform non-blocking IO functions, allowing for sending and receiving messages to occur asynchronously. This ensures that the main application continues running without waiting for these operations to complete, improving the overall efficiency and responsiveness of the application.

Client Design Philosophy

The CPP client is designed mainly around the principle that one can create many "channels", possibly to multiple corelink peer services. These channels could be for control, reporting, heart beating, synchronizing, or for data transfer. However, it is important to note that this distinction is largely logical and only pertinent to Corelink, not the networking layer.

Networking protocols and channels

Protocol support

It is important to realize that almost all that the Corelink client does is provide one with a framework to connect to the Corelink server with appropriate messaging and alleviating the need for one to use bare sockets. Hence, if done correctly, we can make this data transport largely independent of the underlying network protocol. Hence, we separate concerns. We first start by writing a base model for data exchange, and implement this interface for any specific protocol that we might have. This is addressed by the class corelink_data_xchg_protocol. All protocols which are then meant to exchange data (this includes all IP based protocols), implement this interface and allow for a uniform API.

It now allows us to support more data exchange protocols like USB, Thunderbolt, PCIx, I2C, RDMA etc. while still maintaining the same interface, mostly.

Concept of channels

In the client, we coin the term channel to represent an entity which is responsible for doing some action. This could be moving data, driving control events, monitoring etc. Think of it similar to those of event streams, albeit much simpler. In the current context that channels are written, they are meant to have certain attributes and certain functions basis the protocols that they support for "doing" things e.g. message transfer. In the future, a channel which supports ICMP can be easily dropped in for pinging certain peers.

Corelink Client

We have now a solid bedrock to implement a client, which is essentially a group of channels, some utility functions and largely just Corelink logic put together, while being hooked in to the underlying networking interfaces and exposing only a lucid API, which might seem verbose at first, but is extremely expressive and intuitive.

Control channels

Corelink client and server communicate in a largely RESTful fashion, The preferred way of connecting to Corelink control services is via a bidirectional secure WebSocket channel. However, since platform support for WebSockets and HTTP libraries in general are a question, we allow for a TCP control channel. Note however that this TCP channel is unencrypted and data over this channel can be read by anyone on a network.

With the new client, one can add multiple control channels within the same client instance which are all auto managed.

Data Channels

As is with control channels, the client instance exposes the necessary API for sending and receiving data, fault reporting and graceful shutdowns. You can again have multiple data channels which are not strictly tied to the control channels, however, we highly recommend not crossing auth tokens. It can result in server dropping your packets and giving you an impression that the client does not work.

Utility items

We have also dropped in some utility items for convenience, which you are free to enable and use in your project. Things like json wrappers for standard JSON API access, string functions, random number generators etc.

Class diagrams

You can view the hierarchy and diagrams as a part of the Doxygen auto doc gen documentation. Navigate to Classes > Class Hierarchy. And then click on the class of choosing to view the hierarchy.

Old vs New Client

Problems with previous versions

Although the previous version of the C++ client solved the problem, it created a few of its own. To enumerate some -

  1. The client behaviour was tied too closely to the protocols. For instance, the control channels and data channels each had an implementation basis the type of protocol in use. This is an okay abstraction where the number of supported protocols remains fairly small. However, if you decide to support more protocols, some of which may not be over IP, it creates a problem.
  2. It does not correctly or reliably, or in some cases, both, compile cross-platform due to the libraries in use.
  3. Instead of using event loops and async concepts, a lot of the work was done on new threads. This is fine for a few sockets and threads, but does not scale well beyond 4-5 threads. Additionally, these threads freewheeled when they had nothing to do. This wastes CPU cycles and draws massive amounts of power.
  4. Additionally, the C++ client had a more C with classes feel to it.
  5. The previous client lacked configurability. Since everything was tied so close to each other, disabling parts of the client was not really an option. This forced you to work with the whole client even if you were using a small part of it.
  6. DLL Mess. The intentions though noble and great, the implementation was a different story.

Solutions

The new client addresses these problems and is also oriented in such a way that maintenance in the future is smoother. The core idea here is to design components separately. Broadly divided into 3 categories - core (which houses network and memory components), utils, and commons which stand true globally, are all independent parts of the library which can be brought in as and when the need. The stress is on maintaining a uniform API through functions or classes wherever necessary which allows for swapping out components much faster and without breaking changes in most cases. All above components are generic to a certain degree - they are not bound to Corelink in any way. The client is essentially a consumer here, which creates necessary instances and tracks certain tokens, channels and other items while exposing a much simpler API for the consumer of this library.

There are a number of benefits to this approach. The client is entirely agnostic of the C++ networking interface except for the API, hence, as long as this API does not break or change significantly, the client can continue to exist and support and ever-growing and evolving components and networking protocols.

The client has been written in such a way that you would not have to talk to these networking libraries directly. You will almost always be a consumer of the corelink client classes and functions, unless some parts specifically ask you to use networking functions;

The new library also focuses on using modern C++ wherever possible. Standard library, modern C++ idioms, value semantics etc. The corelink library can be compiled in to a separate shared object too, in case the need arises where multiple projects share it. Work is always being done to make sure that shared library boundary communication is seamless and works cross-platform. However, this is not yet fully tested, so we would recommend doing this with caution. As best practice we would recommend compiling the library local to where you would like to use it. This ensures maximum compatibility with local ABIs and APIs.

The entire library now works asynchronously. All of this is achieved using ASIOs service loops or other supported event loops, or C++ futures. Everything else is offloaded on to these and sufficient synchronization, or optimistic concurrency is performed to make sure that you can run multithreaded applications without butting heads with other components. This allows us to serve a lot of channels with only a couple of threads, and sockets limited only limited by your CPU, RAM and NIC bandwidths.

As an aside, we also have internal DNS lookup capability now for all IP based protocols, where any form of URL will be resolved, with IPV4 and V6 support.

The library is also being made modular with each revision. The idea is that you don't use what you don't need. This also results in smaller sizes of binaries and reduced compile times.

Lastly, the client has been written in such a way that it is compatible with C++ standards 11 through 17 for now. As compiler support for C++ 20 improves, we will add features like concepts and coroutines to simplify things further. Additionally, we are working on making the client even lighter than it is now, and allowing C like interfaces if possible. There are no promises made about this so do not hold your breath.