NtpUdpChannel C++ library
v8.1.0
Table of contents
- Overview
- Library files
- Versions
- NtpUdpChannel class description
- Build and connect to your project
- Examples
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 |
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;
}