
UdpSocket C++ library
v3.4.1
Table of contents
- Overview
- Versions
- UdpSocket class description
- Examples
- Build and connect to your project
- Portability
Overview
UdpSocket C++ library provides methods to work with UDP port (open, close, send data and receive data). UdpSocket library is cross-platform and compatible with Windows and Linux OS. Main file UdpSocket.h includes declaration of UdpSocket class which provides methods to work with UDP socket. UdpSocket.h includes only standard C++ headers: the address structure sockaddr_in of the methods read(…), send(…), getIp(…) and getPort(…) is only declared there. Code which creates or reads the addresses includes UdpSocketSystem.h: it includes the system headers of the socket interface for Linux and Windows. The library works with IPv4 addresses. The library requires C++17 standard. The library doesn’t have any third-party dependency.
UdpSocket 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.
Versions
Table 1 - Library versions.
| Version | Release date | What’s new |
|---|---|---|
| 1.0.0 | 10.10.2021 | First version |
| 2.0.0 | 17.05.2022 | - Class interface changed. - Added Tracker library to print debug info. |
| 3.0.0 | 13.05.2023 | - Class interface changed. - Tracer library excluded. |
| 3.0.1 | 13.05.2023 | - Fixed send data to external address. |
| 3.1.0 | 27.08.2023 | - Repository made public. |
| 3.1.1 | 26.03.2024 | - Documentation updated. |
| 3.1.2 | 22.05.2024 | - Documentation updated. |
| 3.1.3 | 17.07.2024 | - CMake updated. |
| 3.2.0 | 09.06.2025 | - Add option for input buffer size (socket option). |
| 3.3.0 | 06.02.2026 | - Add getSendBufferSize method. - Add getSendBufferUsed method. |
| 3.4.0 | 14.07.2026 | - Large receive-buffer requests (bufferBytes) now raise net.core.rmem_max via its sysctl when needed (Linux), and use SO_RCVBUFFORCE when privileged, so the requested size is not silently clamped. Best-effort: needs root/CAP_NET_ADMIN, otherwise the existing limit is kept. |
| 3.4.1 | 16.10.2026 | - Removed the change of net.core.rmem_max (system-wide setting) from the open(…) method. SO_RCVBUFFORCE is still used if the process has the CAP_NET_ADMIN capability. - Fixed leak of the socket (and of the Windows socket library initialization) when the open(…) method fails. - The open(…) method returns FALSE for negative timeoutMsec or bufferBytes and for an invalid dstIp (the dstIp is checked before the socket is created). - The read(…) method changes srcAddr only if data was read. The source address is not stored in the object (fixed data race of concurrent read(…) calls). - The getPort(…) method returns -1 for nullptr. Fixed read of an uninitialized buffer in the getIp(…) method. - The socket is created with the close-on-exec flag (Linux). - Strict compiler warnings, compiler and linker hardening options, standard C++ only. - Examples validate the input, stop after repeated errors and do not leak memory. |
UdpSocket class description
UdpSocket class declaration
UdpSocket interface class declared in UdpSocket.h file. Class declaration:
namespace rapidpixel
{
/// UDP Socket class (IPv4).
class UdpSocket
{
public:
/// Get current library version.
static std::string getVersion();
/// Class constructor.
UdpSocket();
/// Class destructor. Closes the socket.
~UdpSocket();
/// Copy constructor (deleted, the object owns a socket).
UdpSocket(const UdpSocket& src) = delete;
/// Move constructor (deleted, the object owns a socket).
UdpSocket(UdpSocket&& src) = delete;
/// Copy assignment operator (deleted, the object owns a socket).
UdpSocket& operator=(const UdpSocket& src) = delete;
/// Move assignment operator (deleted, the object owns a socket).
UdpSocket& operator=(UdpSocket&& src) = delete;
/// Open UDP socket.
bool open(uint16_t port,
bool serverType = false,
std::string dstIp = "127.0.0.1",
int timeoutMsec = 100, int bufferBytes = 0);
/// Read data.
int read(uint8_t* data,
int size,
sockaddr_in* srcAddr = nullptr);
/// Send data.
int send(uint8_t* data, int size, sockaddr_in* dstAddr = nullptr);
/// Check if UDP socket open.
bool isOpen();
/// Close UDP socket.
void close();
/// Get IP of data source.
std::string getIp(sockaddr_in* srcAddr);
/// Get UDP port of data source.
int getPort(sockaddr_in* srcAddr);
/// Get send buffer size.
int getSendBufferSize();
/// Get current usage of send buffer.
int getSendBufferUsed();
};
}
Thread safety: the isOpen() method can be called from any thread at any time. The open(…) and close() methods must not be called at the same time as any other method of the same object. After the socket is open the read(…), send(…), getSendBufferSize() and getSendBufferUsed() methods can be called from several threads at the same time. Stop the threads that use the socket before close() is called or the object is destroyed.
getVersion method
The getVersion() method returns string of current class version. Method declaration:
static std::string getVersion();
Method can be used without UdpSocket class instance:
std::cout << "UdpSocket v: " << rapidpixel::UdpSocket::getVersion() << std::endl;
Console output:
UdpSocket v: 3.4.1
open method
The open(…) method initializes UDP socket. The method closes the socket if it is open. The socket allows sending data to broadcast addresses (SO_BROADCAST). The method must not be called at the same time as any other method of the same object. Method declaration:
bool open(uint16_t port, bool serverType = false, std::string dstIp = "127.0.0.1", int timeoutMsec = 100, int bufferBytes = 0);
| Parameter | Value |
|---|---|
| port | UDP port. Must have values from 0 to 65535. Local port of the server socket and destination port of the send(…) calls without destination address. |
| serverType | Socket type: TRUE - socket is bound to the port on all local IPv4 interfaces and will be able to read and write data, FALSE - socket is not bound and will be able only send data. |
| dstIp | Destination IPv4 address in dotted decimal notation (for example “127.0.0.1”) to send data. Host names are not resolved. |
| timeoutMsec | Wait data timeout, milliseconds. Must not be negative. Method sets timeout to UDP socket properties. Timeout determines behavior of read(…) method: method will wait input data maximum timeoutMsec milliseconds and will return negative results if no input data. 0 - wait without timeout. |
| bufferBytes | Receive buffer size (SO_RCVBUF), bytes. Must not be negative. 0 - the size set by the OS is not changed. A small input buffer may effect of packet loss if your application is interrupted for short time. The OS may limit the size. On Linux the size is limited by net.core.rmem_max unless the process has the CAP_NET_ADMIN capability (in this case SO_RCVBUFFORCE is used for this socket). The library does not change settings of the OS. |
Returns: TRUE if the UDP port open or FALSE if not (negative timeoutMsec or bufferBytes, invalid dstIp, error of the OS). If FALSE is returned the socket is closed.
read method
The read(…) method designed to read (wait) input data. After receiving input data the method will return control immediately or will return control after timeout (set in open(…) method) if no input data. The method can be called from several threads at the same time. Method declaration:
int read(uint8_t* data, int size, sockaddr_in* srcAddr = nullptr);
| Parameter | Value |
|---|---|
| data | Pointer to data buffer. Must not be nullptr. The pointer must be valid for size bytes. |
| size | Size of data buffer and maximum data size to read from socket. Must have values from 1 to 65536. On Linux the bytes of a datagram that do not fit into the buffer are discarded. |
| srcAddr | Optional pointer to address structure. Method returns address structure of data source. User can use it to send data back to data source. The structure is changed only if data was read. |
Returns: Number of bytes (0 for an empty datagram) or -1 if no input data or timeout expired (set in open(…) method) or UDP socket not open or error or invalid parameter (data is nullptr, size is out of range).
send method
The send(…) method sends data. The method can be called from several threads at the same time. Method declaration:
int send(uint8_t* data, int size, sockaddr_in* dstAddr = nullptr);
| Parameter | Value |
|---|---|
| data | Pointer to data buffer. Must not be nullptr. The pointer must be valid for size bytes. |
| size | Size of data to send. Must have values from 1 to 65536. The OS does not send a datagram with more than 65507 bytes of data. |
| dstAddr | Optional pointer to address structure. If address structure provide method will send data to this address, otherwise data is sent to the address set by open(…). |
Returns: Number of bytes sent or -1 if data not sent or UDP socket not open or invalid parameter (data is nullptr, size is out of range).
isOpen method
The isOpen() method returns UDP socket open status. The method can be called from any thread at any time. Method declaration:
bool isOpen();
Returns: TRUE if UDP socket open or FALSE if not.
close method
The close() method closes socket if it open. The method must not be called at the same time as any other method of the same object. Method declaration:
void close();
getIp method
The getIp(…) method extracts IP address from address structure. Method declaration:
std::string getIp(sockaddr_in* srcAddr);
| Parameter | Value |
|---|---|
| srcAddr | Pointer to address structure. |
Returns: IP string in dotted decimal notation or empty string if srcAddr is nullptr or the address can not be converted.
getPort method
The getPort(…) method extracts UDP port from address structure. Method declaration:
int getPort(sockaddr_in* srcAddr);
| Parameter | Value |
|---|---|
| srcAddr | Pointer to address structure. |
Returns: UDP port or -1 if srcAddr is nullptr.
getSendBufferSize method
The getSendBufferSize() method returns send buffer size (SO_SNDBUF). Method declaration:
int getSendBufferSize();
Returns: Send buffer size in bytes or -1 if error or UDP socket not open.
getSendBufferUsed method
The getSendBufferUsed() method returns current usage of send buffer. Method declaration:
int getSendBufferUsed();
| Platform | Implementation |
|---|---|
| Linux | Returns exact queue size using ioctl with SIOCOUTQ. |
| Windows | Returns the ideal send backlog (WSAIoctl with SIO_IDEAL_SEND_BACKLOG_QUERY, Windows 10 version 1809+). This is not the number of queued bytes. The request is defined for TCP sockets: it may fail for UDP sockets or older Windows SDK versions. |
Returns: Number of bytes currently used in send buffer or -1 if error, UDP socket not open, or feature not supported on the platform.
Examples
Data sender
Test application shows how to create socket only to send data. Test application send test data periodically. The application checks the input values and stops after 10 consecutive send errors.
#include <chrono>
#include <cstdint>
#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include "UdpSocket.h"
namespace
{
/// Maximum UDP port number.
constexpr int MAX_PORT{65535};
/// Maximum size of the data to send, bytes.
constexpr int MAX_NUM_BYTES{8192};
/// Number of consecutive send errors after which the sender stops. The limit
/// prevents an endless loop if the destination is not reachable.
constexpr int MAX_SEND_ERRORS{10};
/// State of the data generator. The data is a test pattern, not a secret.
uint32_t g_randomState{20240915U};
/// Get next byte of the test data (linear congruential generator).
uint8_t nextByte()
{
g_randomState = g_randomState * 1664525U + 1013904223U;
return static_cast<uint8_t>(g_randomState >> 16);
}
}
int main()
{
std::cout << "Data sender v" << rapidpixel::UdpSocket::getVersion() <<
std::endl << std::endl;
std::string ip = "";
std::cout << "Set destination IP: ";
if (!(std::cin >> ip))
{
std::cout << "Wrong destination IP" << std::endl;
return -1;
}
int port = 0;
std::cout << "Set UDP port: ";
if (!(std::cin >> port) || port < 0 || port > MAX_PORT)
{
std::cout << "Wrong UDP port" << std::endl;
return -1;
}
int cyclePeriodMsec = 0;
std::cout << "Set sending data period msec: ";
if (!(std::cin >> cyclePeriodMsec) || cyclePeriodMsec < 0)
{
std::cout << "Wrong period" << std::endl;
return -1;
}
int numBytes = 0;
std::cout << "Set num bytes to send [1-" << MAX_NUM_BYTES << "]: ";
if (!(std::cin >> numBytes) || numBytes < 1 || numBytes > MAX_NUM_BYTES)
{
std::cout << "Wrong number of bytes" << std::endl;
return -1;
}
// Open UDP socket: client mode (only to send data).
rapidpixel::UdpSocket udpSocket;
if (!udpSocket.open(static_cast<uint16_t>(port), false, ip))
{
std::cout << "Can't open UDP socket" << std::endl;
return -1;
}
// Main loop.
std::vector<uint8_t> data(static_cast<size_t>(numBytes));
std::chrono::steady_clock::time_point startTime =
std::chrono::steady_clock::now();
int sendErrors = 0;
while (sendErrors < MAX_SEND_ERRORS)
{
// Prepare test data.
for (uint8_t& value : data)
{
value = nextByte();
}
// Send data.
const int bytes = udpSocket.send(data.data(), numBytes);
std::cout << bytes << " bytes sent" << std::endl;
sendErrors = bytes == numBytes ? 0 : sendErrors + 1;
// Wait according to parameters.
const int elapsedMsec = static_cast<int>(
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - startTime).count());
const int waitMsec = cyclePeriodMsec - elapsedMsec;
if (waitMsec > 0)
{
std::this_thread::sleep_for(std::chrono::milliseconds(waitMsec));
}
startTime = std::chrono::steady_clock::now();
}
std::cout << "Too many send errors" << std::endl;
return 1;
}
Data receiver
Test application shows how to create socket to read and send. Test application receives data and shows info about sender. The application checks the input values and stops after 1000 consecutive reads without data.
#include <cstdint>
#include <iostream>
#include "UdpSocket.h"
#include "UdpSocketSystem.h"
namespace
{
/// Maximum UDP port number.
constexpr int MAX_PORT{65535};
/// Size of the buffer for received data, bytes.
constexpr int BUFFER_SIZE{8192};
/// Number of consecutive reads without data (timeout expired or error) after
/// which the receiver stops. The limit prevents an endless loop if the socket
/// is broken.
constexpr int MAX_EMPTY_READS{1000};
}
int main()
{
std::cout << "Data receiver v" << rapidpixel::UdpSocket::getVersion() <<
std::endl << std::endl;
int port = 0;
std::cout << "Set UDP port: ";
if (!(std::cin >> port) || port < 0 || port > MAX_PORT)
{
std::cout << "Wrong UDP port" << std::endl;
return -1;
}
int timeoutMsec = 0;
std::cout << "Set wait data timeout, msec: ";
if (!(std::cin >> timeoutMsec) || timeoutMsec < 0)
{
std::cout << "Wrong timeout" << std::endl;
return -1;
}
// Open UDP socket: server mode (to receive data).
rapidpixel::UdpSocket udpSocket;
if (!udpSocket.open(static_cast<uint16_t>(port), true, "127.0.0.1",
timeoutMsec))
{
std::cout << "Can't open UDP socket" << std::endl;
return -1;
}
// Main loop.
uint8_t data[BUFFER_SIZE];
int emptyReads = 0;
while (emptyReads < MAX_EMPTY_READS)
{
// Read data. Max wait time = timeoutMsec.
sockaddr_in addr{};
const int bytes = udpSocket.read(data, BUFFER_SIZE, &addr);
// Check input data size.
if (bytes <= 0)
{
std::cout << "No input data" << std::endl;
++emptyReads;
continue;
}
emptyReads = 0;
// Show sender info.
std::cout << bytes << " bytes read from " << udpSocket.getIp(&addr) <<
"/" << udpSocket.getPort(&addr) << std::endl;
}
std::cout << "Too many reads without data" << std::endl;
return 1;
}
Build and connect to your project
Build stand alone
The repository does not contain sub-repositories and the library does not depend on other libraries. Typical commands to build the library and the examples (use the URL of the repository provided to you):
git clone <UdpSocket repository URL>
cd UdpSocket
mkdir build
cd build
cmake ..
make
On Windows use cmake --build . --config Release instead of make. The examples are built into the bin folder of the build folder.
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 UdpSocket is the top-level project).
| Option | Default | Description |
|---|---|---|
| ${PARENT}_UDP_SOCKET | ON | Build the library. |
| ${PARENT}_UDP_SOCKET_EXAMPLES | ON (stand alone), OFF (included in other project) | Build the examples. |
| ${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 UdpSocket as a sub-repository. |
| CMAKE_BUILD_TYPE | Release (if not specified) | Build type. |
Connect to your project
If you want connect UdpSocket 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 and copy UdpSocket repository folder there. New structure of your repository:
CMakeLists.txt
src
CMakeList.txt
yourLib.h
yourLib.cpp
3rdparty
UdpSocket
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()
################################################################################
## INCLUDING SUBDIRECTORIES
## Adding subdirectories according to the 3rd-party configuration
################################################################################
if (${PARENT}_SUBMODULE_UDP_SOCKET)
add_subdirectory(UdpSocket)
endif()
File 3rdparty/CMakeLists.txt adds folder UdpSocket to your project and excludes examples from compiling (by default examples excluded from compiling if UdpSocket included as sub-repository). UdpSocket does not depend on other libraries, so the project does not have to add other libraries before it. Your repository new structure will be:
CMakeLists.txt
src
CMakeList.txt
yourLib.h
yourLib.cpp
3rdparty
CMakeLists.txt
UdpSocket
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 UdpSocket library in your src/CMakeLists.txt file:
target_link_libraries(${PROJECT_NAME} UdpSocket)
Done!
Portability
The library uses the POSIX sockets API on Linux and Winsock 2 on other platforms (the library ws2_32 is linked on Windows). The library works with IPv4 addresses only. Ports and addresses are converted with the network byte order functions, so the library does not depend on the byte order of the CPU. The library was built with GCC 11 and GCC 12 on x86-64 and with GCC 11 for aarch64 (Linux).