
TcpServer C++ library
v2.1.3
Table of contents
- Overview
- Versions
- Library files
- TcpServer class description
- TcpClientInfo class description
- Example
- Build and connect to your project
- Portability
Overview
The TcpServer C++ library provides methods to work with TCP sockets as a server (wait for connections, close connections, send data, and receive data). The TcpServer library is cross-platform and compatible with Windows and Linux operating systems. The library uses the C++17 standard and doesn’t have any third-party dependencies. The main file TcpServer.h includes the declaration of the TcpServer class, which provides methods to work with TCP sockets as a server. The library has an internal thread to wait for connections from clients. All of the library’s methods are thread-safe, which means it is safe to call them from different threads. The library stores the list of connected clients internally, and this list is updated with the read(…) method.
TcpServer 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 | 08.06.2023 | - First version |
| 1.0.1 | 13.10.2023 | - Connection issues fixed. |
| 1.0.2 | 11.03.2024 | - Client reconnection issue fixed. - Documentation updated. |
| 1.0.3 | 16.04.2024 | - Documentation updated. |
| 1.0.4 | 22.05.2024 | - Documentation updated. |
| 1.0.5 | 30.07.2024 | - CMake structure updated. |
| 2.0.0 | 10.09.2024 | - New interface. |
| 2.0.1 | 25.03.2025 | - Fixed frequent connection requests issue. - Fixed detection of disconnected client on Windows. |
| 2.0.2 | 14.04.2025 | - Fixed read timeout behavior. |
| 2.0.3 | 26.08.2025 | - Improved stability and fixed critical bugs. |
| 2.1.0 | 30.10.2025 | - Added additional configuration to reduce TCP ACK delay. |
| 2.1.1 | 21.05.2026 | - Fixed data race on the internal fd_set in read() that could cause periodic 100–200 ms stalls under concurrent accept(). - Re-armed TCP_QUICKACK after each successful recv() so subsequent ACKs are not delayed by the kernel. - Added TCP_THIN_LINEAR_TIMEOUTS and TCP_THIN_DUPACK for faster recovery on packet loss in request/response traffic. - Removed forced SO_RCVBUF / SO_SNDBUF = 8192 to let the kernel auto-tune buffer sizes. - Tuned TCP keep-alive parameters (idle/interval/count = 5/2/3) for reliable peer-loss detection without service-packet flooding. |
| 2.1.2 | 16.10.2026 | - Fixed disconnection of a healthy client by read(…) called with a zero or negative buffer size. - Fixed writing outside of the descriptor set (stack buffer overflow) for descriptors not less than FD_SETSIZE: such connections are rejected. - Limited the number of connected clients to 64. - init(…) rejects ports outside of the range from 0 to 65535. - read(…) and send(…) reject nullptr buffers and non-positive sizes, read(…) rejects a negative timeout. - A TcpClientInfo of a disconnected client is rejected by send(…) and isConnected(…) also when its descriptor is used by another client. - Client sockets are closed once, after the last thread stops using them. Fixed races between read(…), send(…), isConnected(…), getClients(), init(…) and close(). - Concurrent send(…) calls to one client are not mixed, concurrent read(…) calls are executed one after another, clients are served in turn. - An interrupted recv() does not disconnect a client. - When the list of clients is full, the clients that have disconnected are removed from it before a new connection is rejected. - A blocked send(…) returns when read(…) removes the client from the list. - The listening socket does not block: a connection that is reset before accept() cannot stop close(). - Winsock is initialized once for a TcpServer object and released by its destructor (Windows). - Sockets are not inherited by child processes (Windows). - Exceptions do not leave the methods and the connection thread. - Added compiler warning options, hardening options and CMAKE_CXX_EXTENSIONS OFF to the CMake files. - Test application replaced by automated tests. |
| 2.1.3 | 25.10.2026 | - The library links the thread library (CMake target Threads::Threads): programs built on a C library older than glibc 2.34 no longer need -pthread. |
Library files
The library is supplied as source code only. The user receives 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.
README.md ------------------ Documentation of the library.
static --------------------- Folder with images of the documentation.
tcpserver_web_logo.png - Logo of the library.
src ------------------------ Folder with library source code.
CMakeLists.txt --------- CMake file of the library.
TcpServer.h ------------ Main library header file.
TcpServer.cpp ---------- C++ implementation file.
TcpServerVersion.h ----- Header file with library version.
TcpServerVersion.h.in -- File for CMake to generate version header.
test ----------------------- Folder for test application.
CMakeLists.txt --------- CMake file to include test application.
main.cpp --------------- Source code of test application (automated tests).
TcpServer class description
Class declaration
The TcpServer class is declared in the TcpServer.h file. Class declaration:
class TcpServer
{
public:
/// Default constructor.
TcpServer() = default;
/// Destructor.
~TcpServer();
/// Copy and move operations are deleted.
TcpServer(const TcpServer&) = delete;
TcpServer(TcpServer&&) = delete;
TcpServer& operator=(const TcpServer&) = delete;
TcpServer& operator=(TcpServer&&) = delete;
/// Method to get string of current library version.
static std::string getVersion();
/// Initialize TCP server.
bool init(int port);
/// Read data from clients.
int read(uint8_t* buffer, int size, TcpClientInfo& client,
int timeout = 1000);
/// Send data to a client.
int send(uint8_t* buffer, int bufferSize, TcpClientInfo client);
/// Check if client connected.
bool isConnected(TcpClientInfo &client);
/// Get connected clients.
std::vector<TcpClientInfo> getClients();
/// Close TCP socket.
void close();
};
getVersion method
The getVersion() method returns a string with the current library version. Method declaration:
static std::string getVersion();
The method can be used without a TcpServer class instance:
std::cout << "TcpServer version: " << rapidpixel::TcpServer::getVersion();
Console output:
TcpServer version: 2.1.3
init method
The init(…) method is designed to initialize the TCP server. The server listens on the port on all local IPv4 interfaces. If the server is open, the method closes it first. The server keeps at most 64 clients connected at the same time: when the list of clients is full, the clients that have disconnected are removed from it, and a client over the limit is disconnected right after the connection is accepted. Method declaration:
bool init(int port);
| Parameter | Value |
|---|---|
| port | TCP port. Must have a value from 0 to 65535. The value 0 lets the operating system select the port (the library does not report the selected port). |
Returns: TRUE if initialization is successful or FALSE if not (the port is out of range, or the socket cannot be created, bound or put to the listening state, or Winsock cannot be initialized, or the internal thread cannot be started). The server is closed if FALSE is returned.
read method
The read(…) method reads (waits for) input data from a client. After receiving input data, the method will return control immediately, or will return control after a timeout if no input data is received. The method waits for the clients that are connected when it starts: if no client is connected, it waits for the whole timeout, and a client that connects during the call is served by the next call. If several clients have data, they are served in turn. This method also updates the list of connected clients: it removes the clients that have disconnected. Calls of the method from several threads are executed one after another; a call that cannot start within its timeout returns -1. Method declaration:
int read(uint8_t* buffer, int size, TcpClientInfo& client, int timeout = 1000);
| Parameter | Value |
|---|---|
| buffer | Pointer to data buffer. Must not be nullptr. The buffer must be valid for size bytes. |
| size | Size of the buffer (maximum number of bytes to read). Must be greater than 0. |
| client | Reference to TcpClientInfo structure. It will be filled with information about the client that sent the data. It is not changed if no data is read. |
| timeout | Wait timeout for data in milliseconds. Must not be negative. Default value is 1000 ms. |
Returns: Number of bytes received (from 1 to size) or -1 if there is no input data, a parameter is invalid or the server is closed.
send method
The send(…) method sends data to a client. The method blocks while the operating system cannot accept the data (for example, when the client does not read its data). Calls for the same client from several threads are executed one after another. Method declaration:
int send(uint8_t* buffer, int bufferSize, TcpClientInfo client);
| Parameter | Value |
|---|---|
| buffer | Pointer to data buffer. Must not be nullptr. The buffer must be valid for bufferSize bytes. |
| bufferSize | Size of data to send. Must be greater than 0. |
| client | TcpClientInfo structure with client information. It must be obtained from the read(…) or getClients() method and the client must be connected. |
Returns: Number of bytes sent (can be less than bufferSize if sending is interrupted) or -1 if data was not sent (a parameter is invalid, the client is not connected or the connection failed).
isConnected method
The isConnected() method returns the client’s connection status. The list of clients is updated by the read(…) method: the method returns TRUE for a client that has disconnected until read(…) detects it. A structure of a client that is not in the list is not connected, also when another client uses the same socket descriptor. Method declaration:
bool isConnected(TcpClientInfo &client);
| Parameter | Value |
|---|---|
| client | TcpClientInfo structure with client information. |
Returns: TRUE if the client is connected to the server or FALSE if not.
getClients method
The getClients() method returns a list of connected clients (at most 64). This list is updated by the read(…) method. If a client disconnects, the getClients() method returns a list that includes the disconnected client until read(…) detects the disconnection. Method declaration:
std::vector<TcpClientInfo> getClients();
Returns: List of TcpClientInfo structures with connected client information.
close method
The close() method closes the server if it is open: it stops the internal thread that is waiting for incoming client connection requests, disconnects all clients and closes the listening socket. A read(…) call that waits for data from connected clients and a send(…) call that waits for the operating system return when the clients are disconnected. A send(…) call that is blocked for a client also returns when read(…) removes the client from the list. The method can be called for a closed server and from several threads. Method declaration:
void close();
TcpClientInfo class description
TcpClientInfo structure class declaration
The TcpClientInfo structure contains information about a client. The structure is a copy of the data taken at the moment of the call: after the server removes the client from the list of clients (the read(…) method detects that the client has disconnected, or the server is closed), the copy is rejected by the send(…) and isConnected(…) methods. The structure also holds private data that identifies the connection inside the server. Structure declaration:
class TcpClientInfo
{
public:
/// Client IP address (IPv4, dotted decimal notation).
std::string ip;
/// Client port number.
int port{-1};
};
| Field | Type | Description |
|---|---|---|
| ip | string | Client IPv4 address in dotted decimal notation |
| port | int | Client port |
Build and connect to your project
Build stand alone
The library has no dependencies and the repository does not contain sub-repositories. Typical commands to build the library and the test application (use the URL of the repository provided to you):
git clone <TcpServer repository URL>
cd TcpServer
mkdir build
cd build
cmake ..
make
On Windows use cmake --build . --config Release instead of make. The test application is built into the bin folder of the build folder. It starts a server on a local port, connects test clients and checks the behavior of every method; it returns 0 if all tests pass.
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 TcpServer is the top-level project). The CMakeLists.txt file of the repository sets the options on every configuration, so they cannot be changed from the command line.
| Option | Default | Description |
|---|---|---|
| ${PARENT}_TCP_SERVER | ON | Build the library. |
| ${PARENT}_TCP_SERVER_TEST | ON (stand alone), OFF (included in other project) | Build the test application. |
| ${PARENT}_SUBMODULE_CACHE_OVERWRITE | ON | Option of the top-level project. The top-level project sets it to OFF when it includes TcpServer as a sub-repository. |
| CMAKE_BUILD_TYPE | Release (if not specified) | Build type. |
Connect to your project
If you want to connect the TcpServer library to your CMake project as source code, you can follow these steps. For example, if your repository has the following structure:
CMakeLists.txt
src
CMakeLists.txt
yourLib.h
yourLib.cpp
Create a folder named 3rdparty and copy the TcpServer repository folder there. The new structure of your repository:
CMakeLists.txt
src
CMakeLists.txt
yourLib.h
yourLib.cpp
3rdparty
TcpServer
Create a CMakeLists.txt file in the 3rdparty folder. The 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_TCP_SERVER ON CACHE BOOL "" FORCE)
if (${PARENT}_SUBMODULE_TCP_SERVER)
SET(${PARENT}_TCP_SERVER ON CACHE BOOL "" FORCE)
SET(${PARENT}_TCP_SERVER_TEST OFF CACHE BOOL "" FORCE)
endif()
################################################################################
## INCLUDING SUBDIRECTORIES
## Adding subdirectories according to the 3rd-party configuration
################################################################################
if (${PARENT}_SUBMODULE_TCP_SERVER)
add_subdirectory(TcpServer)
endif()
The 3rdparty/CMakeLists.txt file adds the TcpServer folder to your project and excludes the test application from compilation (by default, the test application is excluded from compilation if TcpServer is included as a sub-repository). The new structure of your repository:
CMakeLists.txt
src
CMakeLists.txt
yourLib.h
yourLib.cpp
3rdparty
CMakeLists.txt
TcpServer
Next, you need to include the 3rdparty folder in the main CMakeLists.txt file of your repository. Add the following line at the end of your main CMakeLists.txt:
add_subdirectory(3rdparty)
Next, you have to include the TcpServer library in your src/CMakeLists.txt file:
target_link_libraries(${PROJECT_NAME} TcpServer)
Done!
Example
This example shows how to create a TCP server, wait for data from clients and send the data back. The server stops after 30 read attempts without data (about 30 seconds). The test folder contains the test application of the library.
#include <cstdint>
#include <iostream>
#include <vector>
#include "TcpServer.h"
int main()
{
std::cout << "TcpServer v" << rapidpixel::TcpServer::getVersion() <<
" example" << std::endl;
// Init TCP server.
rapidpixel::TcpServer server;
if (!server.init(9909))
{
std::cout << "Server not initialized" << std::endl;
return -1;
}
constexpr int MAX_EMPTY_READS{30};
constexpr int BUFFER_SIZE{10000};
std::vector<uint8_t> buffer(BUFFER_SIZE);
rapidpixel::TcpClientInfo client;
int emptyReads = 0;
while (emptyReads < MAX_EMPTY_READS)
{
// Wait for data from client (default timeout is 1000 ms).
const int bytes = server.read(buffer.data(), BUFFER_SIZE, client);
if (bytes <= 0)
{
std::cout << "No input data" << std::endl;
++emptyReads;
continue;
}
emptyReads = 0;
std::cout << "Received " << bytes << " bytes from client " <<
client.ip << ":" << client.port << std::endl;
// Send data back to client.
if (server.send(buffer.data(), bytes, client) != bytes)
{
std::cout << "Couldn't send data to client" << std::endl;
}
}
server.close();
return 0;
}
Portability
The library uses BSD sockets on Linux and Winsock 2.2 on Windows (the ws2_32 library is linked automatically). It supports IPv4 only. The data of the clients is transported as bytes without conversion, so the library does not depend on the byte order of the host. The socket options TCP_QUICKACK, TCP_KEEPIDLE, TCP_KEEPINTVL, TCP_KEEPCNT, TCP_THIN_LINEAR_TIMEOUTS and TCP_THIN_DUPACK are set on Linux only. The library was built with GCC 11, GCC 12 and Clang 18 on Linux x86-64 and with GCC 11 for Linux aarch64; the test application was run on these targets. The Windows code was compiled, but not run, with MinGW-w64 for x86, x86-64 and ARM64.