udpdatachannel_web_logo

UdpDataChannel C++ library

v1.2.1

Table of contents

Overview

UdpDataChannel C++ library provides point-to-point communication between two applications based on UDP. The library includes two main classes: UdpDataClient (connects to server) and UdpDataServer (wait connection from client). Both classes provide two-way communication. The library uses C++17 standard and only depends on open source UdpSocket (source code included, Apache 2.0 license) which provide functions to work with UDP sockets. This library compatible with Linux and Window. Server principles: the server initializes the user-specified UDP port and waits for the client to connect from any port. The server supports connection of only one client. When a client connects the server remembers the client’s IP and UDP port to exchange messages with them. The client sends special commands to connect. Client principles: the client initialized any first available UDP port in the OS. After initialization client sends connection messages to server port (to connect the user must say to client the server’s UDP port and IP). Once connection established the client can exchanges messages with server. To send data server and client split data into separate UDP packets with special header data. Receiver (client or server) collect UDP packets and detect when whole input data being send by sender collected. After receiver sends confirmation. Sender sends data twice until it will get confirmation from receiver. The library can handle intensive data exchange close to channel bandwidth limit. The library allows user to set maximum channel bandwidth to prevent network overloading. Sender (client or server) controls interval between UDP packets being sent.

Versions

Table 1 - Library versions.

Version Release date What’s new
1.0.0 20.11.2023 - First version of the library.
1.1.0 12.02.2024 - Repository structure reorganized.
- Added separate classes for client and server.
- Documentation updated.
- Example added.
1.1.1 13.04.2024 - Documentation updated.
1.1.2 22.05.2024 - UdpSocket updated.
- Documentation updated.
1.1.3 11.06.2024 - CPU overloading when no output data fixed.
1.2.0 17.07.2024 - CMake updated.
- Repository structure changed.
- Example updated.
1.2.1 23.07.2024 - Files structure updated.

Library files

The library 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.
3rdparty ------------------------ Folder with third-party libraries.
    CMakeLists.txt -------------- CMake file to include third-party libraries.
    UdpSocket ------------------- Folder with UdpSocket library source code.
src ----------------------------- Folder with the library source code.
    CMakeLists.txt -------------- CMake file of the library.
    UdpDataChannel.cpp ---------- C++ implementation file.
    UdpDataChannel.h ------------ Main header file.
    UdpDataChannelVersion.h ----- Header file which includes library version.
    UdpDataChannelVersion.h.in -- CMake service file to generate version header.
clienttest ---------------------- Folder for client test application.
    CMakeLists.txt -------------- CMake file for client test application.
    main.cpp -------------------- Source code of client test application.
servertest ---------------------- Folder for server test application.
    CMakeLists.txt -------------- CMake file for server application.
    main.cpp -------------------- Source code of server test application.
example ------------------------- Folder for example application.
    CMakeLists.txt -------------- CMake file for example application.
    main.cpp -------------------- Source code of example application.

UdpDataClient and UdpDataServer classes description

UdpDataClient and UdpDataServer classes declaration

UdpDataClient class declared in UdpDataChannel.h file. Class declaration:

namespace cr
{
namespace clib
{
/// UDP data client class.
class UdpDataClient
{
public:

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

    /// Class constructor.
    UdpDataClient();

    /// Class destructor.
    ~UdpDataClient();

    /// Initialize client with server's address and port.
    bool init(std::string serverIp, uint16_t serverPort,
              int channelBandwidthKbps = 1000000);

    /// Close client.
    void close();

    /// Get input data from connected client.
    bool get(uint8_t* data, int bufferSize, int& dataSize, int timeoutMsec = 0);

    /// Send data to the client.
    bool send(uint8_t* data, int size);

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

    /// Get channel status.
    bool isInit();
};
}
}

UdpDataServer class declared in UdpDataChannel.h file. Class declaration:

namespace cr
{
namespace clib
{
/// UDP data server class.
class UdpDataServer
{
public:

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

    /// Class constructor.
    UdpDataServer();

    /// Class destructor.
    ~UdpDataServer();

    /// Initialize server with port number and bandwidth.
    bool init(uint16_t port, int channelBandwidthKbps = 1000000);

    /// Close server.
    void close();

    /// Get input data from connected client.
    bool get(uint8_t* data, int bufferSize, int& dataSize, int timeoutMsec = 0);

    /// Send data.
    bool send(uint8_t* data, int size);

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

    /// Get channel status.
    bool isInit();
};
}
}

getVersion method

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

static std::string getVersion();

Method can be used without UdpDataClient and UdpDataServer class instance:

cout << "UdpDataClient v: " << cr::clib::UdpDataClient::getVersion() << endl;
cout << "UdpDataServer v: " << cr::clib::UdpDataServer::getVersion() << endl;

Console output:

UdpDataClient v: 1.2.0
UdpDataServer v: 1.2.0

init method

The init(…) method of UdpDataClient class initializes client with server IP, UDP port and optional channel bandwidth. The client choses first available in OS UDP port from 20000 to 65535 range. After UDP port initialization the client starts communication threads and will start connection to server. Method declaration:

bool init(std::string serverIp, uint16_t serverPort, int channelBandwidthKbps = 1000000);
Parameter Description
serverIp Server IP. Client will connect to this IP.
serverPort Server UDP port. Client will connect to this port.
channelBandwidthKbps Channel bandwidth in kilobits per second. The client will control time interval between UDP packets to prevent network overload.

Returns: TRUE if the client was successfully initialized or FALSE if not.

The init(…) method of UdpDataServer class initializes server with UDP port and optional channel bandwidth. After UDP port initialization the server starts communication threads and will start listening connection requests from client. Method declaration:

bool init(uint16_t port, int channelBandwidthKbps = 1000000);
Parameter Description
port Server UDP port. Client will connect to this port.
channelBandwidthKbps Channel bandwidth in kilobits per second. The server will control time interval between UDP packets to prevent network overload.

Returns: TRUE if the server was successfully initialized or FALSE if not.

close method

The close(…) method closes client or server and releases all resources. This method stops all threads, closes all sockets and also releases all memory allocated in data buffers. Method declaration:

void close();

get method

The get(…) method returns received data. The library has internal buffer (size of 16 elements). The library put received data to this buffer. When user call get(…) method it returns data from buffer. The buffer can hold several portions of data (up to 16). In case multiple data in input buffer, the method will return the earliest one received. This way the data will not be lost if the user does not have time to read it. If no input data from client or server the method will wait until it will come or particular timeout. The method is thread-safe and can be called from multiple threads. Method declaration:

bool get(uint8_t* data, int bufferSize, int& dataSize, int timeoutMsec = 0);
Parameter Description
data A pointer to a buffer where the received data will be stored.
bufferSize The size of the buffer allocated for storing the received data. If input data has bigger size than buffer the method will return FALSE.
dataSize Size of input data. If no input data size will be 0.
timeoutMsec The timeout period for receiving data, in milliseconds. Values:
less 0 - wait until data will come.
0 - just check if we have new data.
more 0 - wait timeout, msec.

Returns: TRUE if there is new data or FALSE if not.

send method

The send(…) method sends data. The library has internal buffer (size of 16 elements). The library puts data to the output buffer. Internal thread reads data from buffer, splits by UDP packets and sends them. This way the data will not be lost if the user calls the method multiple times. The method is thread-safe and can be called from multiple threads. Method declaration:

bool send(uint8_t* data, int size);
Parameter Description
data A pointer to the data buffer containing the data to be sent.
size Data size to send. Maximum size is 67108863 bytes. If size more than 67108863 bytes the method will return FALSE.

Returns: TRUE if the data accepted to send or FALSE if not.

isConnected method

The isConnected(…) method return connection status between client and server. After initialization the client tries connect to server and server waits connection from client. Once the client connected to server the method will be returning TRUE. Method declaration:

bool isConnected();

Returns: TRUE if the client is currently connected to the server or FALSE if not.

isInit method

The isInit(…) method return client or server initialization status. Method declaration:

bool isInit();

Returns: TRUE if the client is initialized or FALSE if not.

Build and connect to your project

Typical commands to build the library:

cd UdpDataChannel
mkdir build
cd build
cmake ..
make

If you want to connect UdpDataChannel to your CMake project as source code you can follow these steps. For example, if your repository has structure:

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

Create folder 3rdparty in you repository and copy UdpDataChannel repository folder there. The new structure of your repository:

CMakeLists.txt
src
    CMakeList.txt
    yourLib.h
    yourLib.cpp
3rdparty
    UdpDataChannel

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_DATA_CHANNEL                ON  CACHE BOOL "" FORCE)
if (${PARENT}_SUBMODULE_UDP_DATA_CHANNEL)
    SET(${PARENT}_UDP_DATA_CHANNEL                      ON  CACHE BOOL "" FORCE)
    SET(${PARENT}_UDP_DATA_CHANNEL_CLIENT_TEST          OFF CACHE BOOL "" FORCE)
    SET(${PARENT}_UDP_DATA_CHANNEL_SERVER_TEST          OFF CACHE BOOL "" FORCE)
    SET(${PARENT}_UDP_DATA_CHANNEL_EXAMPLE              OFF CACHE BOOL "" FORCE)
endif()

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

File 3rdparty/CMakeLists.txt adds folder UdpDataChannel to your project and excludes test applications from compiling (by default test applications and example excluded from compiling if UdpDataChannel included as sub-repository). The new structure of your repository:

CMakeLists.txt
src
    CMakeList.txt
    yourLib.h
    yourLib.cpp
3rdparty
    CMakeLists.txt
    UdpDataChannel

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 UdpDataChannel library in your src/CMakeLists.txt file:

target_link_libraries(${PROJECT_NAME} UdpDataChannel)

Done!

Example

This examples demonstrates how to use server and client. The application creates thread for server which waits data from client and send responses. Client sends random data to server in main thread and waits response from server. Source code of example:

#include <iostream>
#include <thread>
#include <cstdint>
#include "UdpDataChannel.h"

void serverThreadFunction()
{
    // Init server.
    cr::clib::UdpDataServer server;
    if (!server.init(58002))
    {
        std::cout << "Can't init server" << std::endl;
        return;
    }

    // Thread loop.
    uint8_t buffer[256];
    while (true)
    {
        // Wait data from client for 2 sec.
        int size = 0;
        if (!server.get(buffer, 256, size, 2000))
        {
            std::cout << "No input data from client" << std::endl;
            continue;
        }

        // Send data back to client.
        if (!server.send(buffer, size))
            std::cout << "Can't send data to client" << std::endl;
    }
}

int main(void)
{
    // Run server thread.
    std::thread serverThread(&serverThreadFunction);

    // Init client.
    cr::clib::UdpDataClient client;
    if (!client.init("127.0.0.1", 58002))
    {
        std::cout << "Can't init client" << std::endl;
        return -1;
    }

    // Main loop.
    uint8_t buffer[256];
    while (true)
    {
        // Prepare random data for server.
        int size = (rand() % 128) + 1;
        memset(buffer, (rand() % 255), size);
        std::cout << std::endl;

        // Send data to server.
        if (!client.send(buffer, size))
            std::cout << "Can't send data to server" << std::endl;
        else
            std::cout << size << " bytes to server" << std::endl;

        // Wait data from server.
        size = 0;
        if (!client.get(buffer, 256, size, 2000))
            std::cout << "No input data from server" << std::endl;
        else
            std::cout << size << " bytes from server" << std::endl;
    }

    return 1;
}