ntpdatachannel_web_logos

NtpUdpChannel C++ library

v8.1.0

Table of contents

Overview

NtpUdpChannel C++ library simplifies sending and receiving data over the UDP transport layer via low bandwidth networks. The library streams data taking into account channel bandwidth and controls data loss. The library depends on UdpSocket library (provides methods to work with UDP sockets, source code included, Apache 2.0 license). It also provides applications to test communication via network. The library utilizes C++17 standard. The library doesn’t have any third-party dependency to be specially installed in the OS. The library is compatible with Linux and Windows OS.

Versions

Table 1 - Library versions.

Version Release date What’s new
7.2.0 22.05.2022 - Protocol parser changed.
8.0.0 28.02.2024 - Interface changed.
- Documentation updated.
8.0.1 16.04.2024 - Documentation updated.
8.0.2 22.05.2024 - UdpSocket updated.
- Documentation updated.
8.1.0 30.07.2024 - CMake structure updated.
- NtpParser moved to implementation folder.

Library files

The library is supplied only by source code. The user is 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 ------------------ Files with UdpSocket library source code. 
src ---------------------------- Library source code folder.
    CMakeLists.txt ------------- CMake file of the library.
    NtpUdpChannel.h ------------ Main library header file.
    NtpUdpChannelVersion.h ----- Header file with library version.
    NtpUdpChannelVersion.h.in -- File for CMake to generate version header.
    NtpUdpChannel.cpp ---------- Main C++ implementation file.
    impl
        NtpParser.h ------------ NTP protocol parser header file.
        NtpParser.cpp ---------- NTP protocol parser source file.
examples ----------------------- Folder with examples
    CMakeLists.txt ------------- CMake file to include examples.
    NtpUdpChannelReceiver ------ Folder with data receiver example.
        CMakeLists.txt --------- CMake file of data receiver example.
        main.cpp --------------- Source code of data receiver example.
    NtpUdpChannelSender -------- Folder with data sender example.
        CMakeLists.txt --------- CMake file of data sender example.
        main.cpp --------------- Source code of data sender example.
    NtpUdpChannelBenchmark ----- Folder with benchmark application.
        CMakeLists.txt --------- CMake file of benchmark application.
        main.cpp --------------- Source code of benchmark application.

NtpUdpChannel class description

Class declaration

NtpUdpChannel class declared in NtpUdpChannel.h file. Class declaration:

class NtpUdpChannel 
{
public:
    // Get string of current library version.
    static std::string getVersion();

    // Class constructor.
    NtpUdpChannel();

	// Class destructor.
    ~NtpUdpChannel();

	// Init communication channel.
    bool init(std::string config);

	// Write data to channel.
    bool write(uint8_t* data, uint32_t dataSize, int32_t logicPort = 0,
               uint32_t resendsCount = 0, float bandwidthMbps = 0,
               int32_t waitConfirmedMs = 0);

	// Read data from channel.
    bool read(uint8_t* dataBuff, uint32_t dataBuffSize,
              uint32_t& inputSize, int32_t& logicPort,
              int32_t timeoutMs = 0);

	// Get open status.
    bool isOpen();
}

getVersion method

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

static std::string getVersion();

Method can be used without NtpUdpChannel class instance:

std::cout << "NtpUdpChannel version: " << NtpUdpChannel::getVersion();

Console output:

Serial port version: 8.1.0

init method

The init(…) method to initialize communication channel. Method declaration:

bool init(std::string config);
Parameter Value
config Initialization string contains;; separated by "**;**". Example: **"50001;192.168.1.1;50002"**. To provide communication sender and receiver used two UDP ports: one to receive data and one to send data.

Returns: TRUE if the communication channel init or FALSE if not.

write method

The write(…) method send data to communication channel. Method declaration:

bool write(uint8_t* data, uint32_t dataSize, int32_t logicPort = 0,
           uint32_t resendsCount = 0, float bandwidthMbps = 0,
           int32_t waitConfirmedMs = 0);
Parameter Value
data Pointer to the data to send.
dataSize Size of data to send.
logicPort Logic port number to send data. The library supports up to 255 logit ports.
resendsCount Number of send repetitions.
bandwidthMbps Bandwidth of communication channel in Mbit/s. Method controls sending bandwidth.
value == 0 - the method will send data as fast as possible.
waitConfirmedMs Timeout in milliseconds to wait sent and confirmation of the data.
value == -1 - method will wait indefinitely until data is sent;
value == 0 - method will not wait for the data to be sent;
value > 0 - method will wait the specified time until the data to be sent and confirmed;

Returns: TRUE if data was sent and confirmed (if is confirmed). FALSE if data was not sent or not confirmed (if is confirmed).

read method

The read(…) method reads data from communication channel. Method will wait timeoutMs and will return all data (<= requested amount of data) from communication channel. Method declaration:

bool read(uint8_t* dataBuff, uint32_t dataBuffSize,
          uint32_t& inputSize, int32_t& logicPort,
          int32_t timeoutMs = 0);
Parameter Value
dataBuff Pointer to buffer.
dataBuffSize Size of buffer. Size of data which you want to read from communication channel.
inputSize Size of readed data.
logicPort Logic port number to receive data. The library supports up to 255 logit ports.
timeoutMs Timeout in ms to wait for data:
value == -1 - method will wait indefinitely until data arrives;
value == 0 - the method will only check for new data;
value > 0 - the method will wait specified time.

Returns: TRUE if data is and copied or FALSE if no new data or the data has already been read.

isOpen method

The isOpen() method intended to obtain communication channel open status. Method declaration:

bool isOpen();

Returns: TRUE is the channel open or FALSE if not.

Build and connect to your project

Typical commands to build **NtpUdpChannel ** library (on Linux OS):

cd NtpUdpChannel
mkdir build
cd build
cmake ..
make

If you want connect NtpUdpChannel library to your CMake project as source code you can make follow. For example, if your repository has structure:

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

Create 3rdparty folder in your repository folder and copy NtpUdpChannel repository folder there. created folder. New structure of your repository:

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

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_NTP_UDP_CHANNEL                 ON  CACHE BOOL "" FORCE)
if (${PARENT}_SUBMODULE_NTP_UDP_CHANNEL)
    SET(${PARENT}_NTP_UDP_CHANNEL                       ON  CACHE BOOL "" FORCE)
    SET(${PARENT}_NTP_UDP_CHANNEL_EXAMPLES              OFF CACHE BOOL "" FORCE)
endif()

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

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

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

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

target_link_libraries(${PROJECT_NAME} NtpUdpChannel)

Done!

Examples

Data sender example

#include <iostream>
#include <cstring>
#include <thread>
#include <ctime>
#include <chrono>
#include <fstream>

#include "NtpUdpChannel.h"
using namespace cr::clib;

// Entry point.
int main(void)
{
    std::cout<< "=================================================" << std::endl;
    std::cout<< "NtpUdpChannelSender "                              << std::endl;
    std::cout<< "=================================================" << std::endl;
    std::cout<< "Library versions: "                                << std::endl;
    std::cout<< "NtpUdpChannel:....."<< NtpUdpChannel::getVersion() << std::endl;
    std::cout<< "-------------------------------------------------" << std::endl;
    std::cout<< std::endl;

    std::string answer = "n";
    std::cout << "Host UDP port\t: 50002" << std::endl;
    std::cout << "Dst IP Addr\t: 127.0.0.1" << std::endl;
    std::cout << "Dst UDP port\t: 50001" << std::endl;
    std::cout << "Sending period\t: 100 ms" << std::endl;
    std::cout << "Use default settings ? (y|n): ";
    std::cin >> answer;

    std::string initString = "";

    // Enter host UDP port.
    int hostUdpPort = 50002;
    if (!(answer == "y" || answer == "Y")){
        std::cout << "Enter host UDP port: ";
        std::cin >> hostUdpPort;
    }
    initString += std::to_string(hostUdpPort) + ";";

    // Enter destination IP.
    std::string dstIp = "127.0.0.1";
    if (!(answer == "y" || answer == "Y")){
        std::cout << "Enter destination IP: ";
        std::cin >> dstIp;
    }
    initString += dstIp + ";";

    // Enter destination UDP port.
    int dstUdpPort = 50001;
    if (!(answer == "y" || answer == "Y")){
        std::cout << "Enter destination UDP port: ";
        std::cin >> dstUdpPort;
    }
    initString += std::to_string(dstUdpPort) + ";";

    // Enter sending data period [ms].
    int sendDataTimeoutMs = 0;
    if (!(answer == "y" || answer == "Y")){
        std::cout << "Enter sending data period, ms: ";
        std::cin >> sendDataTimeoutMs;
    }
    // Init pure UDP channel.
    cr::clib::NtpUdpChannel* channel = new cr::clib::NtpUdpChannel();
    if (!channel->init(initString))
    {
        std::cout << "ERROR: UDP channel not init" << std::endl;
        return -1;
    }

    // Calculate and init data buffer.
    uint32_t outputSize = 1024; // Any value.
    uint8_t* data = new uint8_t[outputSize];
    int32_t logicPort = 0;
    uint32_t resendsCount = 1;
    float bandwidthMbps = 10;
    uint32_t waitConfirmedMs = -1;
    int count = 0;
    while (true)
    {
        memset(data, count++, outputSize);

        // Send data.
        if(!channel->write(data, outputSize, logicPort, resendsCount,
                              bandwidthMbps, waitConfirmedMs))
        {
            std::cout << "ERROR: Send data problem" << std::endl;
            break;
        }
        else {
            std::cout << "Send " << outputSize << " bytes" << std::endl;
        }

        // Wait sendDataTimeout ms for new data.
        uint32_t inputSize = 0;
        int32_t logicPort = 0;
        int32_t timeoutMs = 100;
        if (!channel->read(data,outputSize,inputSize,logicPort,timeoutMs)) {
            std::cout << "No respond" << std::endl;
        }else if (inputSize > 0) {
            std::cout << "Get " << inputSize << " bytes" << std::endl;
        }

        std::this_thread::sleep_for(std::chrono::milliseconds(sendDataTimeoutMs));
    }

    delete[] data;
    return 1;
}

Data receiver example

#include <iostream>
#include <cstring>
#include <thread>
#include <ctime>
#include <chrono>
#include <fstream>

#include "NtpUdpChannel.h"
using namespace cr::clib;

// Entry point.
int main(void)
{
    std::cout<< "=================================================" << std::endl;
    std::cout<< "NtpUdpChannelReceiver "                            << std::endl;
    std::cout<< "=================================================" << std::endl;
    std::cout<< "Library versions: "                                << std::endl;
    std::cout<< "NtpUdpChannel:....."<< NtpUdpChannel::getVersion() << std::endl;
    std::cout<< "-------------------------------------------------" << std::endl;
    std::cout<< std::endl;

    std::string answer = "n";
    std::cout << "Host UDP port\t: 50001" << std::endl;
    std::cout << "Dst IP Addr\t: 127.0.0.1" << std::endl;
    std::cout << "Dst UDP port\t: 50002" << std::endl;
    std::cout << "Waiting period\t: 1000 ms" << std::endl;
    std::cout << "Use default settings ? (y|n): ";
    std::cin >> answer;

    std::string initString = "";

    // Enter host UDP port.
    int hostUdpPort = 50001;
    if (!(answer == "y" || answer == "Y")){
        std::cout << "Enter host UDP port: ";
        std::cin >> hostUdpPort;
    }
    initString += std::to_string(hostUdpPort) + ";";

    // Enter destination IP.
    std::string dstIp = "127.0.0.1";
    if (!(answer == "y" || answer == "Y")){
        std::cout << "Enter destination IP: ";
        std::cin >> dstIp;
    }
    initString += dstIp + ";";

    // Enter destination UDP port.
    int dstUdpPort = 50002;
    if (!(answer == "y" || answer == "Y")){
        std::cout << "Enter destination UDP port: ";
        std::cin >> dstUdpPort;
    }
    initString += std::to_string(dstUdpPort) + ";";

    // Enter the waiting period for input data, ms.
    int waitDataTimeoutMs = 1000;
    if (!(answer == "y" || answer == "Y")){
        std::cout << "Enter the waiting period for input data, ms: ";
        std::cin >> waitDataTimeoutMs;
    }

    // Init pure UDP channel.
    cr::clib::NtpUdpChannel* channel = new cr::clib::NtpUdpChannel();
    if (!channel->init(initString))
    {
        std::cout << "ERROR: UDP channel not init" << std::endl;
        return -1;
    }

    // Calculate and init data buffer.
    uint32_t buffsize = 1024; // Any value.
    uint8_t* buff = new uint8_t[buffsize];
    uint32_t inputSize = 0;

    // Main loop.
    while (true)
    {
        // Wait Timeout ms for new data.
        int32_t logicPort = 0;
        if (channel->read(buff, buffsize, inputSize, logicPort, waitDataTimeoutMs))
        {
            std::cout << "Get " << inputSize << " bytes" << std::endl;

            // Send respond.
            if (!channel->write(buff, inputSize))
            {
                std::cout << "ERROR: Send respond problem" << std::endl;
                break;
            }
            else
            {
                std::cout << "Send " << inputSize << " bytes" << std::endl;
            }
        }
        else
        {
            std::cout << "No data" << std::endl;
        }
    }

    delete[] buff;
    return 1;
}