A header-only library for building asynchronous network services.
- Header-only - No compilation required, just include and go
- TCP and UDP support - Both protocols with the same consistent API
- C++20 compiler (GCC 10+, Clang 13+, MSVC 2022+)
- CMake 3.28+
- Ninja (recommended build system)
- Linux (other platforms may work but are untested)
Add cppnet to your CMake project using CPM:
include(cmake/CPM.cmake)
CPMAddPackage("gh:kcexn/cppnet@0.9.0")
target_link_libraries(your_target PRIVATE cppnet)include(FetchContent)
FetchContent_Declare(
cppnet
GIT_REPOSITORY https://github.com/kcexn/cppnet.git
GIT_TAG v0.9.0
)
FetchContent_MakeAvailable(cppnet)
target_link_libraries(your_target PRIVATE cppnet)git clone https://github.com/kcexn/cppnet.git
cd cppnet
cmake --preset release
sudo cmake --install build/releaseSome simple examples of applications built with cppnet:
#include <net/cppnet.hpp>
#include <arpa/inet.h>
using namespace net::service;
// Define your service by inheriting from async_tcp_service
struct echo_service : public async_tcp_service<echo_service> {
using Base = async_tcp_service<echo_service>;
// Constructor passes address to base class
template <typename T>
explicit echo_service(socket_address<T> address) : Base(address) {}
// Optional: configure socket options
auto initialize(const socket_handle &socket) -> std::error_code {
// Set socket options here if needed
return {};
}
// Optional: handle graceful shutdown
auto stop() -> void {
// Cleanup code here
}
// Handle incoming data - MUST call submit_recv() to continue
auto service(async_context &ctx, const socket_dialog &socket,
std::shared_ptr<read_context> rctx,
std::span<const std::byte> buf) -> void {
using namespace io::socket;
using namespace stdexec;
// Echo the data back
sender auto echo_sender =
io::sendmsg(socket, socket_message{.buffers = buf}, 0) |
then([&, socket, rctx](auto &&) {
submit_recv(ctx, socket, std::move(rctx)); // Continue reading
}) |
upon_error([](auto &&) {});
ctx.scope.spawn(std::move(echo_sender));
}
};
int main() {
// Create IPv4 address on port 8080
auto addr = io::socket::socket_address<sockaddr_in>();
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = INADDR_ANY;
addr->sin_port = htons(8080);
// Create and start the service in its own thread
auto ctx = basic_context_thread<echo_service>();
ctx.start(addr);
// Wait for termination (service runs until SIGTERM/SIGINT)
ctx.state.wait(async_context::STARTED);
return 0;
}#include <net/cppnet.hpp>
#include <arpa/inet.h>
using namespace net::service;
struct udp_echo_service : public async_udp_service<udp_echo_service> {
using Base = async_udp_service<udp_echo_service>;
template <typename T>
explicit udp_echo_service(socket_address<T> address) : Base(address) {}
auto service(async_context &ctx, const socket_dialog &socket,
std::shared_ptr<read_context> rctx,
std::span<const std::byte> buf) -> void {
using namespace io::socket;
using namespace stdexec;
// Echo back to sender (address is in rctx->msg.address)
sender auto echo_sender =
io::sendmsg(socket, rctx->msg, 0) |
then([&, socket, rctx](auto &&) {
submit_recv(ctx, socket, std::move(rctx));
}) |
upon_error([](auto &&) {});
ctx.scope.spawn(std::move(echo_sender));
}
};
int main() {
auto addr = io::socket::socket_address<sockaddr_in>();
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = INADDR_ANY;
addr->sin_port = htons(8080);
auto ctx = basic_context_thread<udp_echo_service>();
ctx.start(addr);
// Wait for termination
ctx.state.wait(async_context::STARTED);
return 0;
}class client_factory {
public:
using async_context = net::service::async_context;
using context_thread = net::service::context_thread;
struct client_t {
async_context *ctx = nullptr;
// Relevant client methods can be implemented here.
};
auto make_client() -> client_t
{
if (ctx_.state == async_context::PENDING)
ctx_.start();
return {.ctx = std::addressof(ctx_)};
}
private:
context_thread ctx_;
};# Clone the repository
git clone https://github.com/kcexn/cloudbus-net.git
cd cloudbus-net
# Debug build (with tests and coverage)
cmake --preset debug
cmake --build --preset debug
# Release build (optimized)
cmake --preset release
cmake --build --preset release
# Run tests
ctest --preset debug --output-on-failureGenerate API documentation with Doxygen:
cmake --preset debug -DCPPNET_BUILD_DOCS=ON
cmake --build --preset debug --target doxygenDocumentation will be in build/debug/docs/html/index.html.
The library uses the CRTP (Curiously Recurring Template Pattern) for services:
async_context- Execution context with async_scope, I/O multiplexer, signal handling, and event loop timerscontext_thread<Service>- Runs a service in a dedicated thread (defaultnull_serviceis useful for network clients)async_tcp_service<Handler>- TCP server base class with accept/read loopasync_udp_service<Handler>- UDP server base class with read looptimers<InterruptSource>- Event-loop timers for scheduling callbacks
Your service inherits from the appropriate template and implements:
service()to handle received data (required - must callsubmit_recv()to continue)initialize()to configure the socket (optional)stop()for graceful shutdown (optional, TCP only)
Services support two signals:
terminate(0) - Graceful shutdownuser1(1) - Custom application signal
Send signals via async_context::signal(int signum).
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
Contributions are welcome! Please ensure:
- Code follows C++20 best practices
- All tests pass (
ctest --preset debug) - New features include tests and documentation
- Code coverage is maintained
All dependencies are automatically fetched via CPM:
- NVIDIA stdexec (main branch) - Sender/receiver framework
- AsyncBerkeley (v0.4.1) - Async socket operations
- GoogleTest (v1.17.0) - Testing framework (tests only)