boberclient_web_logo

BoberClient C++ library

v1.0.4

Table of contents

Overview

The BoberClient is a C++ library that serves as a communication client for user applications, enabling interaction with any other service through BoberManager. It provides an interface for sending and receiving messages, managing connections, and handling acknowledgments, allowing user applications to communicate with devices, services, or other components in the system. Additionally BoberClient formats outgoing messages, assigns them unique IDs and validates incoming messages. It supports various message types: Command, Request, Notify, Response, and handles acknowledgments (Ack) and errors (Nack) automatically. The library runs a dedicated input thread to process incoming messages without blocking the main application, ensuring thread-safe access to message buffers. It also includes a ping mechanism to maintain connection status. The library is using C++17 and depends on UdpSocket library (provides methods to work with UDP sockets) and Logger library (provides methods to work with logs). The source code of these libraries is not part of the repository, see the section Build and connect to your project. Communication principle:

BoberClient is general-purpose software: it is not tied to any sector or application, and the developer of a program decides what the program does with it.

boberclient_principle

Versions

Table 1 - Library versions.

Version Release date What’s new
1.0.0 10.04.2025 - First version of the library.
1.0.1 22.06.2025 - Updated UdpSocket submodule.
1.0.2 07.02.2026 - Updated UdpSocket submodule.
1.0.3 17.07.2026 - Updated UdpSocket submodule.
1.0.4 17.10.2026 - Sub-repositories (UdpSocket, Logger) removed from the repository: the libraries are expected next to the library folder or must be added by the top-level project.
- get(): the call with a timeout waits for the whole timeout (the call could return FALSE too early); a waiting call returns FALSE when close() is called.
- init(): returns FALSE if there is no free UDP port, if the input thread can’t be started or if the name contains the symbol ‘/’.
- send(): returns FALSE if the datagram was not sent, if the name or the ID contains the symbol ‘/’ or if the message is longer than 65507 bytes; the ID is changed only if the message was sent.
- Incoming messages longer than 2048 bytes are rejected with Nack message (they were cut). A message which does not fit in the input queue is not acknowledged.
- Thread safety: setLogMode() is thread-safe; close() can be called at the same time with send().
- Compiler warnings and security options added to the CMake files; the test application compile error fixed; the test application and the benchmark application stop when the input is closed.

Library files

The library is supplied by source code only. The user would be given a set of files in the form of a CMake project (repository). The repository structure is shown below:

CMakeLists.txt --------------- Main CMake file of the library.
static ----------------------- Folder with images of this document.
src -------------------------- Folder with the library source code.
    CMakeLists.txt ----------- CMake file of the library.
    BoberClient.cpp ---------- C++ library implementation file.
    BoberClient.h ------------ Header library file.
    BoberClientVersion.h ----- Header file which includes library version.
    BoberClientVersion.h.in -- CMake service file to generate version header.
benchmark -------------------- Folder with benchmark application.
    CMakeLists.txt ----------- CMake file of the application.
    main.cpp ----------------- Source code file of benchmark application.
test ------------------------- Folder with test application.
    CMakeLists.txt ----------- CMake file of the application.
    main.cpp ----------------- Source code file of test application.

Message types

The BoberClient library supports different types of messages:

  1. Command - Used to send specific instructions or commands to another service or client. The receiver will send Ack message back if the message accepted or Nack message if something wrong with message.
  2. Request - Used to request specific information or actions from another service or client. The receiver will send Response message back or Nack message if something wrong with message.
  3. Response - Used to respond to a previously received Request message.
  4. Notify - Used to send notifications or updates to other services or clients. The receiver will send Ack message back.
  5. Ack - Acknowledgment message sent to confirm the successful receipt of a message. There are no reply messages for Response.
  6. Nack - Negative acknowledgment message sent to indicate an error or invalid message format. If the client for whom the message is intended is not in the list, the BoberManager sends the Nack message back.

Message format

The BoberClient library uses a specific message format for communication with other clients and services. The format is as follows:

[<ID>]/Src:<source>/Dst:<destination>/<type>/<content>

• : A unique identifier for the message. This helps in tracking and correlating messages. • : The source client name. This indicates the client that sent the message. • : The destination client name. This indicates the client that should receive the message. • : The type of message: Command, Request, Notify, Response, Ack or Nack (see the section **Message types**). A receiving BoberClient rejects a message of any other type with **Nack** message. • : The actual content of the message. The symbol '/' is the field separator, so it must not be used in the names, in the ID and in the content: a receiving BoberClient rejects such a message. Ack and Nack messages have no content.

Examples:

  1. Client List Request:

       [121]/Src:ClientA/Dst:BoberManager/Request/Clients
    

    • [121]: Message ID • Src:ClientA: Source client is ClientA • Dst:BoberManager: Destination is BoberManager • Request/Clients: Requesting the list of connected clients Example response:

       [121]/Src:BoberManager/Dst:ClientA/Response/Clients:ClientB,ClientC
    
  2. Request parameter message to Another Client:

       [122]/Src:ClientA/Dst:ClientB/Request/Contrast
    

    • [122]: Message ID • Src:ClientA: Source client is ClientA • Dst:ClientB: Destination client is ClientB • Request/Contrast: Requesting Contrast parameter from ClientB Example response:

       [122]/Src:ClientB/Dst:ClientA/Response/Contrast:50
    
  3. Command message to set parameter value message to Another Client:

       [123]/Src:ClientA/Dst:ClientB/Command/Contrast:78
    

    • [123]: Message ID • Src:ClientA: Source client is ClientA • Dst:ClientB: Destination client is ClientB • Command/Contrast:78: Calling command to set parameter value Example response:

       [123]/Src:ClientB/Dst:ClientA/Ack
    

BoberClient class description

The BoberClient class provides a communication interface for user applications to interact with the BoberManager and other services in the system. It facilitates sending and receiving messages over UDP, managing connections, and handling message acknowledgments. The class hides the handling of the UDP socket from the application.

BoberClient class declaration

BoberClient interface class declared in BoberClient.h file. Class declaration:

namespace rapidpixel
{
/// BoberClient class.
class BoberClient
{
public:

    /// Get current library version.
    static std::string getVersion();

    /// Class constructor.
    BoberClient();

    /// Class destructor. Closes the connection.
    ~BoberClient();

    /// Copy and move are deleted.
    BoberClient(const BoberClient&) = delete;
    BoberClient(BoberClient&&) = delete;
    BoberClient& operator= (const BoberClient&) = delete;
    BoberClient& operator= (BoberClient&&) = delete;

    /// Initialize BoberClient channel for communication.
    bool init(std::string managerIP, std::string boberClientName);

    /// Send a message.
    bool send(std::string dstName,
              std::string type,
              std::string payload,
              std::string& id);

    /// Get an input message from the BoberClient.
    bool get(std::string& srcName,
             std::string& type,
             std::string& payload,
             std::string& id,
             int timeoutMs = 0);

    /// Get connection status.
    bool isConnected();

    /// Set chosen flag how to log debug info.
    void setLogMode(rapidpixel::PrintFlag printFlag);

    /// Close connection.
    void close();
};
}

getVersion method

The getVersion() method returns string of current class version. Method declaration:

static std::string getVersion();

Method can be used without BoberClient class instance:

std::cout << "BoberClient v: " << rapidpixel::BoberClient::getVersion() << std::endl;

Console output:

BoberClient v: 1.0.4

init method

The init(…) method initializes BoberClient channel for communication. The method closes the channel if it was initialized before, opens the first free UDP port in the range [20000, 65535] and starts the input thread. Messages are sent to the UDP port 62000 of BoberManager. Method declaration:

bool init(std::string managerIP, std::string boberClientName);
Parameter Value
managerIP BoberManager IPv4 address in dotted decimal notation (for example “127.0.0.1”). Host names are not supported.
boberClientName Current service’s name, that is being initialized. The name must not be empty and must not contain the symbol ‘/’ (it is the field separator of the message).

Returns: TRUE if init with success, or FALSE if not. FALSE is returned if the IP or the name is not valid (the state of the object is not changed), if there is no free UDP port or if the input thread can’t be started (the channel is closed).

Thread safety: the method must not be called at the same time with send(…), init(…) or close().

send method

The send(…) method sends a message to a specified destination service. The message includes a type, payload, and a unique ID. If no ID is provided, the method generates a new one. For Notify messages the ID is always generated. Method declaration:

bool send(std::string dstName,
          std::string type,
          std::string payload,
          std::string& id);
Parameter Value
dstName The name of the destination service to which the message will be sent. The name must not be empty and must not contain the symbol ‘/’.
type The type of the message: Command, Request, Notify, Response or Nack.
payload The content or payload of the message. The payload must not be empty. A receiver rejects a message with the symbol ‘/’ in the payload (the symbol is the field separator).
id The unique ID of the message. If an empty string is provided, a new ID is generated: the next value of a 16-bit counter (0..65535, then 0 again). A given ID must not contain the symbol ‘/’. The value is changed only if the method returns TRUE.

Returns: TRUE if the message was sent with success, or FALSE if not. FALSE is returned if the channel is not connected to BoberManager (see isConnected()), if an argument is not valid, if the message is longer than 65507 bytes (the maximum size of the payload of a UDP datagram) or if the datagram was not sent.

Thread safety: the method is thread-safe. It must not be called at the same time with init(…).

get method

The get() method retrieves the next available message from the input queue. If no message is available, it waits for a specified timeout or indefinitely based on the provided timeout value. Messages are returned in the order of arrival. The input queue holds up to 512 messages: a message which does not fit is rejected with Nack message. The maximum size of an incoming message is 2048 bytes: a longer message is rejected with Nack message (on Windows a datagram longer than 2049 bytes can not be read and is ignored without Nack message). Only Command, Request, Notify and Response messages are returned: Ack and Nack messages are not returned. Method declaration:

bool get(std::string& srcName,
         std::string& type,
         std::string& payload,
         std::string& id,
         int timeoutMs = 0);
Parameter Value
srcName The source service name of the retrieved message.
type The type of the retrieved message (Command, Request, Notify or Response).
payload The payload/content of the retrieved message.
id The unique ID of the retrieved message.
timeoutMs The timeout in milliseconds to wait for a message: <0 - wait indefinitely until a message is available, 0 - check immediately if a message is available without waiting, >0 - wait for the specified timeout in milliseconds.

Returns: TRUE if the message was retrieved with success, or FALSE if not (there is no message or a waiting call is interrupted by close()). The output arguments are changed only if TRUE is returned.

Thread safety: the method is thread-safe.

isConnected method

The isConnected() method returns client connection status. The channel is connected after the client receives a message (at least 21 bytes) from BoberManager or from any other sender: the source address is not checked. If the client receives nothing, it sends Ping message about every 2 seconds, and the channel is disconnected after three periods without input data. Method declaration:

bool isConnected();

Returns: TRUE if channel is connected to BoberManager or FALSE if not.

Thread safety: the method is thread-safe.

setLogMode method

The setLogMode(…) method sets chosen flag how to log debug info. The log is disabled by default. Method declaration:

void setLogMode(rapidpixel::PrintFlag printFlag);
Parameter Value
printFlag Print flag: disable, console, file or console and file.

Thread safety: the method is thread-safe.

close method

The close() method closes connection: it stops the input thread, closes the UDP socket and removes all messages from the input queue. The call can take up to one second. The method wakes up the threads which wait in get(…). Method declaration:

void close();

Thread safety: the method must not be called at the same time with init(…) or close(). It can be called at the same time with send(…).

Build and connect to your project

Build stand alone

The repository does not contain sub-repositories. The libraries BoberClient depends on (UdpSocket and Logger) must be cloned into the same folder as BoberClient. The folders must be named UdpSocket and Logger. The folder structure must be:

<work folder>
    BoberClient
    UdpSocket
    Logger

Typical commands to build the library, the test application and the benchmark application (on Linux OS, use the URLs of the repositories provided to you):

git clone <BoberClient repository URL>
git clone <UdpSocket repository URL>
git clone <Logger repository URL>
cd BoberClient
cmake -B build
cmake --build build

On Windows use cmake --build build --config Release. If BoberClient is the top-level CMake project, the main CMakeLists.txt file adds the folders ../UdpSocket and ../Logger before the library itself. If a folder does not exist CMake stops with an error. The applications are built into the folder build/bin.

CMake options

Table 2 - CMake options of the repository. In the options below ${PARENT} is the prefix defined by the top-level project (empty if BoberClient is the top-level project).

Option Default Description
${PARENT}_BOBER_CLIENT ON Build the library.
${PARENT}_BOBER_CLIENT_TEST ON (stand alone), OFF (included in other project) Build the test application.
${PARENT}_BOBER_CLIENT_BENCHMARK ON (stand alone), OFF (included in other project) Build the benchmark application.
${PARENT}_SUBMODULE_CACHE_OVERWRITE ON Forced rewriting of the options above in the CMake cache. The top-level project sets it to OFF when it includes BoberClient as a sub-repository.
CMAKE_BUILD_TYPE Release (if not specified) Build type.

Connect to your project

If you want connect BoberClient library to your CMake project as source code you can make the following. The libraries UdpSocket and Logger must be added to your project together with BoberClient: BoberClient does not add them when it is included in another project. For example, if your repository has structure:

CMakeLists.txt
src
    CMakeList.txt
    yourLib.h
    yourLib.cpp

Create 3rdparty folder in your repository and copy the repository folders BoberClient, UdpSocket and Logger there. The folders must be named as shown. Structure of your repository:

CMakeLists.txt
src
    CMakeList.txt
    yourLib.h
    yourLib.cpp
3rdparty
    BoberClient
    UdpSocket
    Logger

Create CMakeLists.txt file in 3rdparty folder. CMakeLists.txt should contain:

cmake_minimum_required(VERSION 3.13)

################################################################################
## 3RD-PARTY
## dependencies for the project
################################################################################
project(3rdparty LANGUAGES CXX)

################################################################################
## SETTINGS
## basic 3rd-party settings before use
################################################################################
# To inherit the top-level architecture when the project is used as a submodule.
SET(PARENT ${PARENT}_YOUR_PROJECT_3RDPARTY)
# Disable self-overwriting of parameters inside included subdirectories.
SET(${PARENT}_SUBMODULE_CACHE_OVERWRITE OFF CACHE BOOL "" FORCE)

################################################################################
## CONFIGURATION
## 3rd-party submodules configuration
################################################################################
SET(${PARENT}_SUBMODULE_UDP_SOCKET                      ON  CACHE BOOL "" FORCE)
if (${PARENT}_SUBMODULE_UDP_SOCKET)
    SET(${PARENT}_UDP_SOCKET                            ON  CACHE BOOL "" FORCE)
    SET(${PARENT}_UDP_SOCKET_EXAMPLES                   OFF CACHE BOOL "" FORCE)
endif()

SET(${PARENT}_SUBMODULE_LOGGER                          ON  CACHE BOOL "" FORCE)
if (${PARENT}_SUBMODULE_LOGGER)
    SET(${PARENT}_LOGGER                                ON  CACHE BOOL "" FORCE)
    SET(${PARENT}_LOGGER_EXAMPLE                        OFF CACHE BOOL "" FORCE)
endif()

SET(${PARENT}_SUBMODULE_BOBER_CLIENT                    ON  CACHE BOOL "" FORCE)
if (${PARENT}_SUBMODULE_BOBER_CLIENT)
    SET(${PARENT}_BOBER_CLIENT                          ON  CACHE BOOL "" FORCE)
    SET(${PARENT}_BOBER_CLIENT_TEST                     OFF CACHE BOOL "" FORCE)
    SET(${PARENT}_BOBER_CLIENT_BENCHMARK                OFF CACHE BOOL "" FORCE)
endif()

################################################################################
## INCLUDING SUBDIRECTORIES
## Adding subdirectories according to the 3rd-party configuration
################################################################################
if (${PARENT}_SUBMODULE_UDP_SOCKET)
    add_subdirectory(UdpSocket)
endif()

if (${PARENT}_SUBMODULE_LOGGER)
    add_subdirectory(Logger)
endif()

if (${PARENT}_SUBMODULE_BOBER_CLIENT)
    add_subdirectory(BoberClient)
endif()

File 3rdparty/CMakeLists.txt adds the folders UdpSocket, Logger and BoberClient (in this order) to your project and excludes the test and benchmark applications from compiling (by default they are excluded from compiling if BoberClient included as sub-repository). If the dependencies are missing in the top-level project the compilation stops with the error “UdpSocket.h: No such file or directory”. Your repository structure will be:

CMakeLists.txt
src
    CMakeList.txt
    yourLib.h
    yourLib.cpp
3rdparty
    CMakeLists.txt
    BoberClient
    UdpSocket
    Logger

Next you need include folder 3rdparty in main CMakeLists.txt file of your repository. Add string at the end of your main CMakeLists.txt:

add_subdirectory(3rdparty)

Next you have to include BoberClient library in your src/CMakeLists.txt file:

target_link_libraries(${PROJECT_NAME} BoberClient)

Done!

Example application

The BoberClient project includes two executable applications that can be built. The first is a benchmark application, designed to measure the routing time between two clients. The second is a test application, which serves as an example implementation. In the test application, users can specify their service name, destination, and message, and observe the response along with the measured response time. Both applications stop if the input is closed. The code for the example test application is provided below:

#include <iostream>
#include <chrono>
#include <string>
#include "BoberClient.h"
#include "Logger.h"

int main(void)
{
    std::cout << "BoberClient test v" << rapidpixel::BoberClient::getVersion() << std::endl;
    std::cout << std::endl;
    rapidpixel::BoberClient boberClient;

    const int timeout = 1000;

    // Get system IP.
    std::string systemIP{ "127.0.0.1" };
    std::cout << "Set system IP: ";
    if (!(std::cin >> systemIP))
    {
        std::cout << "No input data" << std::endl;
        return -1;
    }

    // Get name of current service.
    std::string myName = "BoberClient";
    std::cout << "Set name of your service: ";
    if (!(std::cin >> myName))
    {
        std::cout << "No input data" << std::endl;
        return -1;
    }

    // Get command destination.
    std::string destinationName = "Dst";
    std::cout << "Set destination name: ";
    if (!(std::cin >> destinationName))
    {
        std::cout << "No input data" << std::endl;
        return -1;
    }

    // Get option if disable print.
    std::string printFlag;
    std::cout << "Disable debug info (y/n): ";
    if (!(std::cin >> printFlag))
    {
        std::cout << "No input data" << std::endl;
        return -1;
    }

    if (printFlag == "y" || printFlag == "Y")
    {
        boberClient.setLogMode(rapidpixel::PrintFlag::DISABLE);
    }
    else
    {
        boberClient.setLogMode(rapidpixel::PrintFlag::CONSOLE);
    }

    // Initialize connection.
    if (!boberClient.init(systemIP, myName))
    {
        std::cout << "Can't initialize the client!" << std::endl;
        return -1;
    }

    std::string srcName;
    std::string type;
    std::string payload;
    std::string receivedID;
    std::chrono::time_point<std::chrono::steady_clock> start;
    int responseTime = 0;
    int sentID = 0;

    // The loop ends when the input is closed.
    while (true)
    {
        sentID++;

        std::cout << "Your message type (Command or Request): ";
        if (!(std::cin >> type))
        {
            break;
        }
        std::cout << "Your message payload: ";
        if (!(std::cin >> payload))
        {
            break;
        }

        // Start counter.
        start = std::chrono::steady_clock::now();

        // Send data.
        std::string id = std::to_string(sentID);
        if (!boberClient.send(destinationName, type, payload, id))
        {
            std::cout << "Can't send data" << std::endl;
        }

        if (!boberClient.get(srcName, type, payload, receivedID, timeout))
        {
            std::cout << "No response in " << timeout << " ms" << std::endl;
            continue;
        }
        responseTime = static_cast<int>(std::chrono::duration_cast<std::chrono::milliseconds>(
            std::chrono::steady_clock::now() - start).count());
        // Show response.
        std::cout << "[RX " << responseTime << " ms] " << "[" + receivedID + "]/" +
            srcName + "/" + type + "/" + payload << std::endl;
    }

    return 0;
}

The application output will look like:

BoberClient test v1.0.4

Set system IP: 127.0.0.1
Set name of your service: BoberClientB
Set destination name: BoberManager
Disable debug info (y/n): y
Your message type (Command or Request): Request
Your message payload: Clients
[RX 0 ms] [1]/BoberManager/Response/Clients:BoberClient,BoberClientB
Your message type (Command or Request):

Portability

The library uses standard C++17 and the libraries UdpSocket and Logger for the system interfaces. The messages are text, so the library does not depend on the byte order of the processor or on the size of the integer types. The library was built and run on Linux with GCC 9, 11 and 12 for x86-64 and with GCC 11 for aarch64.


Table of contents