Introducing the Cute Server

Cute is a next-gen server for developing modern connected systems.

Connected systems are everywhere. Apps interact with servers to enrich the user experience. Systems have their architecture expressed as a set of microservices communicating over networks. Successful software design leads to systems composed of clusters of independent components collaborating through well-defined interfaces to achieve a common goal. This pattern creates systems that embrace change which is the most important feature of maintainable software.

Modern connected systems need efficient ways to establish communication among both their components and peers.

Qt started its life as a UI toolkit that revolutionized UI programming through its signals and slots mechanism. This mechanism standardizes communication between objects that know nothing about each other. With the signals and slots mechanism, developers can devise loosely coupled, highly cohesive systems as a set of independent objects collaborating through signals and slots.

As objects communicating through signals and slots know nothing about their surroundings, they become pluggable. Developers can freely replace them, leading to software that easily adapts to change, an all-important feature for its success.

The signals and slots mechanism shows its power when viewed as a communication protocol. They are intuitive and easy to understand. Also, they promote loosely coupled systems. But seamlessly representing asynchronous interaction is the most important characteristic of the signals and slots mechanism.

Thus, the signals and slots mechanism is a perfect candidate for representing network-based interaction. It promotes true two-way collaboration instead of a request/response model. Objects can communicate state changes with signals and provide functionality with slots. Custom types enable developers to build tailor-made languages among collaborating peers in place of the standardized messages and data exchange formats commonly used for network-based communication.

The Cute server is an innovative product that enables developers to seamlessly use the signals and slots mechanism to communicate over a network.

With the Cute server and SDKs, developers implement server-side functionality through classes containing specially tagged signals and slots that clients interact with as if they were local. The Cute server frees developers from all the technical details of servers, connections, sockets, message security, and high-performance network data processing, making developers much more productive at writing connected systems.

Traditional network-based communication relies on handling requests on the server-side while clients make requests and wait for responses. The request/response model binds clients to servers as servers expect requests with specific characteristics that clients must be aware of to communicate with the server. This binding produces brittle, fragile systems that are very sensitive to changes, an anti-pattern that we want to avoid.

With the Cute server, developers can establish signal-slot connections over a network between objects that know nothing about each other. QObject-derived classes with signals and slots tagged as remote are mapped to endpoints on the server upon registration. Clients only need to provide the endpoint, the class name, and the signature of remote signals and slots to establish remote signal-slot connections and call remote slots directly across a network. SDKs allow clients to interact with remote signals and slots as if they were local.

The Cute server uses epoll-based event dispatchers and custom data streamers to provide a safe and highly performant messaging system. Developers only have to apply a couple of macros to expose QObject-derived classes on the server.

When using multiple workers on multicore machines, the Cute server can use the BPF to enforce packet locality when processing network data, increasing its performance under extreme loads. The Cute server also supports fetching connection information from the proxy protocol header (both v1 and v2 are supported). It is also possible to fetch the connection information from the HTTP requests used during the WebSocket handshake. In addition, the Cute server supports HTTP-based interaction through slots tagged as HTTP handlers.

As an example to highlight how it works, consider the simple Calculator class shown below (the example is intentionally simple to highlight how remote signals and slots work):

#include "Integer.h"
#include <QObject>

class Calculator : public QObject
{
Q_OBJECT
public:
    Calculator() = default;
    ~Calculator() override = default;

public slots:
    qint32 addIntegers(qint32 a, qint32 b);

signals:
    void overflow(Integer a, Integer b);
};

The addIntegers slot is implemented as follows:

#include "Calculator.h"

qint32 Calculator::addIntegers(qint32 a, qint32 b)
{
    if ((a > 0 && b > 0 && (a+b) < 0)
        || (a < 0 && b < 0 && (a+b) > 0))
    {
        emit overflow(Integer(a), Integer(b));
        return -1;
    }
    else
        return a+b;
}

The Calculator class provides the functionality of adding integers. It also emits the overflow signal if the addition operation overflows. In this case, the slot returns -1. The overflow signal uses the Integer class as a custom type for its arguments.

As shown below, just minor modifications are required to make the Calculator class available to remote peers requiring integer addition to fulfill their responsibility on the system.

#include "Integer.h"
#include <CuteServer.h>
#include <QSharedPointer>

class Calculator : public QObject
{
Q_OBJECT
public:
    Calculator(QSharedPointer<Cute::IConnectionInformation> ci)
    {Q_UNUSED(ci)}
    ~Calculator() override = default;

public slots:
    REMOTE_SLOT qint32 addIntegers(qint32 a, qint32 b);

signals:
    REMOTE_SIGNAL void overflow(Integer a, Integer b);
};

And in a source file:

// Developers register classes to the Cute server by
// mapping them to endpoints.
REGISTER_REMOTE_OBJECT("/calculator", Calculator);

Here the Cute server shows its power by completely abstracting away everything related to network programming. The Cute server and SDKs hide from collaborating objects that they are interacting over a network. Developers only need a couple of macros to tag signals and slots as remote and expose classes containing remote signals and slots to clients. The implementation remains unchanged.

Clients use the RemoteObject class to interact with remote objects on the server. The RemoteObject class creates a remote object on the server and acts as a proxy to the created object. Clients interact with remote signals and slots as if they belonged to the RemoteObject class. For example, clients can consume the services provided by the Calculator class as follows:

#include "Integer.h"
#include <CuteClient.h>
#include <QCoreApplication>

using namespace Cute::Client;

class CalculatorClient : public QObject
{
Q_OBJECT
public:
    CalculatorClient(qint32 a, qint32 b)
        : m_calculator("Calculator",
                       QUrl("cute://127.0.100.125:1234/calculator"))
    {
        // Establish remote signal-slot connection
        RemoteObject::connect(&m_calculator,
            SIGNAL(overflow(Integer,Integer)),
            this,
            SLOT(onOverflow(Integer,Integer)));
        // Call the remote slot directly
        m_slotResponse = m_calculator.callRemoteSlot("addIntegers", a, b);
        QObject::connect(m_slotResponse.data(),
            &IRemoteSlotResponse::responded,
            [](const QVariant &response) {
            qWarning("Sum is %d.", response.value<qint32>());
            QCoreApplication::quit();});
    }

public slots:
    // Remote signal-slot connections require public slots.
    void onOverflow(const Integer &a, const Integer &b)
    {
        qWarning("Adding %d to %d overflows.", a.value(), b.value());
    }

private:
    RemoteObject m_calculator;
    QSharedPointer<IRemoteSlotResponse> m_slotResponse;
};

The CalculatorClient class shown above creates a signal-slot connection to the remote overflow signal. Then it requests an integer addition by directly calling the remote addIntegers slot. There is a tutorial that presents the Calculator example in more detail.

The Cute servers and SDKs completely abstract away from developers everything related to network programming. They empower C++/Qt developers to seamlessly communicate over a network using only the signals and slots mechanism, lowering the bar for developing modern connected systems.

Under the hood, the Cute servers and SDKs use WebSockets. Client SDKs share WebSocket connections among remote objects, reducing the load on the server, but clients can create exclusive WebSocket connections for data-hungry objects.

The Cute Client and Server SDKs expose an extremely concise public interface consisting of a couple of macros, functions, and classes. Client SDKs are available for Linux, macOS, Windows, Android, iOS, and WebAssembly. All Cute server editions are Linux-only. Developers use the Cute SDKs by including a single header file and linking to the respective client/server library.

Cute provides servers/SDKs for both Qt5 and Qt6.

With the Cute servers and SDKs, developers implement modern connected systems by exposing, on the server, remote objects that clients interact with as if they were local. The Cute SDKs hide away from clients everything related to network programming. Clients interact with remote objects as if they were local, asynchronous objects.

The Cute server has three Linux-based editions targeting different audiences:

  • Startup: US$79/month edition for indie developers and small businesses.
  • Business: US$129/month or US$1295/year edition targeting larger businesses.
  • Enterprise: US$1895/year edition for enterprises requiring extreme performance.

All Cute server editions use a file for configuration.

The Cute servers and SDKs documentation are available on the Learning Hub.

The concept of object-based communication over a network is not a new thing. The Common Object Request Broker Architecture (CORBA), introduced in 1991, was one of the first attempts to standardize object-based communication. Many other technologies followed, like .Net remoting and Java RMI. These technologies did not succeed as they had two major flaws: complexity and performance.

The Cute server enables developers to use what they already know (Qt's signals and slots) to communicate over a network transparently. There is no learning curve for using signals and slots to communicate over a network. Developers write server-side code as a set of QObject-derived classes containing specially-tagged signals and slots. Client-side code consists of creating instances of the RemoteObject class and interacting with the remote signals and slots of the remote object as if they belonged to the RemoteObject class instead. From the client's point of view, instances of the RemoteObject class are just local, asynchronous objects.

Also, the Cute server uses epoll-based event dispatchers (with Qt, custom event dispatchers inherit the QAbstractEventDispatcher class) to provide high-performance network data processing while supporting the signals and slots mechanism.

In addition, it is important to note that the Qt Remote Objects (QtRO), developed by The Qt Company, is not a similar solution to what the Cute server provides. The QtRO employs the concept of source and replicas to establish a peer-to-peer network. A source exposes an object instance to clients. All clients connecting to the same endpoint interact with the same source object. Also, note that using external QIODevices with QtRO is cumbersome and requires direct usage of a QTcpServer. The default event dispatcher used by Qt processes socket data with a linear time complexity algorithm (O(N)). The Cute server uses epoll, which monitors socket descriptors at O(1).

Server performance is not critical for QtRO because it aims at creating peer-to-peer networks instead of the client/server model used by the Cute server. Also, the Cute server maps exposed classes instead of instances to endpoints. Whenever a client connects to an endpoint, the Cute server creates a new remote object. Also, regarding security, the Cute server uses custom data streamers to handle malicious clients sending rogue data to the server. Through error handlers, developers can take action whenever suspicious activities occur at the network level.

With the Cute server and Cute client SDKs, instead of creating/parsing messages and interacting with a server/clients, developers only have to write QObject-derived classes and establish remote signal-slot connections or call remote slots directly to communicate over a network.

AWS users can evaluate all paid Cute server editions for 30 days on EC2/Lightsail. A single server instance is allowed during the evaluation period. AWS users only have to download and install the chosen server edition on an EC2/Lightsail instance. The Cute server limits usage based on the AWS Account ID. Thus, the instance metadata service must not be disabled (it is enabled by default).

The Cute server enterprise edition is load tested with 1M connections to highlight its performance on another post. You can reproduce the load test on your own AWS infrastructure to help you in evaluating the Cute server.

Also, another post presents a CMake toolchain streamlining cross-compilation for Android, iOS, and WebAssembly. Developers can use this toolchain to deploy client software for different platforms.

The evaluation period is an excellent opportunity to validate the Cute server according to your specific needs.