mynet is an ultra-high-performance, modern C++26 asynchronous network framework and protocol toolkit designed for Linux. Built from the ground up for microsecond-level latency, zero-copy/zero-allocation hot paths, and robust software engineering practices, mynet provides dual I/O engines (Reactor powered by edge-triggered epoll and Proactor powered by io_uring with kernel buffer rings), unified stream and datagram transports (TCP, TLS, UDP), an HTTP/2 wire-compatible stream multiplexer, and strongly-typed error semantics.
- Key Features
- Architecture
- Core Components
- Benchmark Results
- Dependencies & Prerequisites
- Building & Installation
- Quick Start Guide
- Project Layout
- Testing & Quality Assurance
- License
-
Modern C++26 Paradigm:
- Leverages C++26 Preconditions / Contracts (
-fcontracts) for strict caller assertion. - Informative compile-time deletion reasons via
= delete("reason")(P2573R2). - Modern concepts & constraints (
std::derived_from,std::same_as, custom requires clauses). -
std::expectedandstd::optionalreturn pathways eliminating magic numbers.
- Leverages C++26 Preconditions / Contracts (
-
Dual Asynchronous Engines:
-
Reactor: Edge-Triggered
epoll(EPOLLET) witheventfdcross-thread wakeups and a lock-free/lazy-cancelling$O(1)$ priority timer queue. -
Proactor: Pure asynchronous
io_uringutilizing multishot accepts (io_uring_prep_accept), multishot receives (io_uring_prep_recv_multishot), kernel-managed registered buffer rings (io_uring_setup_buf_ring), and poll-driven TLS.
-
Reactor: Edge-Triggered
-
Zero-Allocation Hot Paths:
- Address-stable fixed-capacity
ObjectPool<T>preventing memory reallocation and pointer invalidation during event dispatch. - Sliding-window auto-compacting
Bufferdesigned for long-lived connections.
- Address-stable fixed-capacity
-
Transparent Security:
- First-class OpenSSL 3.x integration with zero code duplication between plain TCP and TLS pipelines.
- Full support for ALPN negotiation, SNI host matching, and asynchronous TLS handshakes.
-
Protocol Engineering & Framing:
- HTTP/2 wire-compatible 9-byte frame codec (
Length:24,Type:8,Flags:8,StreamID:31). - Generalized
Multiplexer<StreamHandler>supporting concurrent stream management, priority, ping, reset, connection/stream-level flow control (WINDOW_UPDATE), and gracefulGOAWAYteardown. - HTTP/1.x codec powered by
llhttpsupporting pipelining and keep-alive.
- HTTP/2 wire-compatible 9-byte frame codec (
-
Observability & Operability:
- Exhaustive enum class error codes (
ErrandIoEnd). - Clean separation: data flows over stdout, structured diagnostic logging routes to
stderrviamynet::log(wrapping submodulemylog), preventing stream pipeline corruption.
- Exhaustive enum class error codes (
The following diagram illustrates the layered architecture of mynet:
flowchart TD
subgraph App ["Application & Protocol Handlers"]
direction LR
HandlerEcho["HandlerBase (Echo)"]
HandlerHttp["HandlerHttp (HTTP/1.x)"]
Mux["Multiplexer<StreamHandler> (HTTP/2 Framing)"]
UdpEcho["DatagramHandler (UDP Echo)"]
end
subgraph Core ["Connection & Transport Abstractions"]
direction LR
Conn["Connection<Handler>"]
Session["Proactor::Session"]
PeerCl["Client<Event> (Client)"]
Datagram["DatagramReply"]
end
subgraph Engine ["Dual I/O Engines"]
direction LR
ReactorEngine["Reactor (epoll + EPOLLET + TimerQueue + eventfd)"]
ProactorEngine["Proactor (io_uring + multishot recv + buf_ring + poll TLS)"]
end
subgraph Foundation ["Memory, Primitives & Diagnostics"]
direction LR
Pool["ObjectPool<T>"]
Buf["Buffer"]
Concepts["concepts"]
Err["Err & IoEnd"]
Log["mynet::log (mylog -> stderr)"]
end
App --> Core
Core --> Engine
Engine --> Foundation
| Feature | Reactor (Reactor<Event>) |
Proactor (Proactor<Event>) |
|---|---|---|
| Kernel Subsystem | Linux epoll (Edge-Triggered, EPOLLET) |
Linux io_uring |
| Notification Model | Readiness Notification | Asynchronous Completion Notification |
| Buffer Management | Userspace Buffer per Connection |
Kernel-managed registered buffer ring (io_uring_buf_ring) |
| Syscall Overhead | One epoll_wait + subsequent non-blocking recv/send |
Batch completion via io_uring_wait_cqe, multishot syscall-free reads |
| TLS Integration | Direct non-blocking OpenSSL SSL_read/SSL_write |
Poll-driven readiness mask (io_uring_prep_poll_add) to avoid ciphertext detachment |
| Cross-Thread Dispatch | eventfd wakeup + loop-bound task queue (runInLoop) |
std::stop_source / submission ring scheduling |
Connection<HandlerType> unifies socket state, TLS encryption, protocol handling, and outgoing write queues:
- Transport Symmetry: Plain TCP and encrypted TLS share the same data path (
doRecv/doSend). TLS is treated as a transport property rather than requiring a separate loop or wrapper. - Pipelining Safety: Responses reside in an internal
std::deque<std::string>write queue by value. Multiple responses generated from a single batch read are guaranteed to arrive in-order without overwriting shared buffers. - Backpressure: Integrated high-watermark inspection and
readCredit()hooks pause reads when downstream consumer queues become saturated. - Reference-Counted Lifetime in Proactor: In
Proactor, connections are wrapped insideSessionstructs trackinginFlightring operations. Connections are never returned to the pool while the kernel has outstanding writes or reads.
ObjectPool<Object>: Pre-allocates a contiguous array of slots viastd::unique_ptr<Slot[]>with an index-based free stack (std::vector<size_t>). Objects are pinned in memory so epoll data pointers and io_uring completion context pointers remain 100% stable, completely bypassing heap allocation during connection accept/release cycles.Buffer: A contiguous dynamic buffer with an offset-based read cursor. Storage is compacted in-place only when dead prefix space exceeds 4 KiB and occupies at least half of the allocation, amortizing compaction overhead while keeping memory footprints bounded.
- Wire Compatibility:
FrameDecoderandappendFrameparse and construct 9-byte HTTP/2 standard frames (DATA,HEADERS,PRIORITY,RST_STREAM,SETTINGS,PUSH_PROMISE,PING,GOAWAY,WINDOW_UPDATE). Multiplexer<StreamHandler>: Enables many independent logical request/response streams over a single byte stream.- Implements credit-based flow control at both connection and stream levels.
- Automatically sends
WINDOW_UPDATEframes when window thresholds drop below half. - Handles concurrent stream capacity limits via
SETTINGS_MAX_CONCURRENT_STREAMSand rejects over-capacity requests withRST_STREAM. - Dispatches to individual
StreamHandlerinstances sourced from anObjectPool.
DatagramHandler: Unlike connected stream sockets, UDP sockets are connectionless and must handle interleaved peers on a single file descriptor.DatagramHandlerassociates peer addresses (sockaddr_in) directly with message buffers (DatagramReply).- Overload Handling: Configurable reply queues drop overflow datagrams with explicit metrics logging, preventing unbounded memory growth under sudden traffic spikes.
- No Magic Numbers:
enum class ERR : uint8_t: Comprehensive, library-wide runtime errors (ADDR_INVALID,SOCKET_FAILED,SSL_CTX_FAILED,URING_BUF_RING_FAILED, etc.) returned viaResult<T = void>(std::expected<T, ERR>).enum class IO_END : uint8_t: Explicit, non-error I/O state explanations (WOULDBLOCK,INTERRUPTED,EOF_REACHED,FATAL) returned viastd::expected<size_t, IO_END>.
- Precondition Contracts: Contract violations (
contract_assert) catch programmer errors (e.g., null pointers, out-of-range port numbers) at translation boundaries.
Client<Event> is an idiomatic Unix-style pipeline client bridging stdin/stdout and network sockets:
cat request.bin | ./client_tcp 127.0.0.1 8888 > response.binFeatures watermark-based backpressure for stdin, graceful TCP/TLS half-close forwarding (close_notify / SHUT_WR), signal-safe blocking of SIGPIPE, and detection of regular file vs interactive terminal descriptors.
Benchmarks run on Linux (x86_64, GCC 15, Release build -O2, single process, with logging redirected away from the measurement path):
benchmark latency throughput
--------------------------------------------------------------------------------
Buffer::append+retrieveAll (1KiB) 11.0 ns/op 92,950.8 MB/s
Buffer::append (16B) to 1KiB + drain 3.0 ns/op --
Buffer::append+retrieve (16B) 3.2 ns/op 5,059.1 MB/s
appendFrame (payload 16B) 4.7 ns/op 3,430.8 MB/s
appendFrame (payload 1024B) 12.7 ns/op 80,516.0 MB/s
appendFrame (payload 16384B) 161.1 ns/op 101,672.5 MB/s
FrameDecoder::next (payload 16B) 12.8 ns/op 1,249.5 MB/s
FrameDecoder::next (payload 1024B) 82.3 ns/op 12,442.8 MB/s
FrameDecoder::next (payload 16384B) 1135.2 ns/op 14,433.3 MB/s
read+appendBigEndian32 0.6 ns/op --
ObjectPool acquire+release (128) 6.9 ns/op --
HandlerBase::pushResponse+takeResponse (1KiB) 23.2 ns/op 44,127.1 MB/s
HandlerBase::process echo (1KiB) 33.4 ns/op 30,664.0 MB/s
Multiplexer open+echo+close (64 streams) 590.4 ns/op --
- Operating System: Linux (Kernel >= 5.19 recommended for full
io_uringbuffer ring and multishot support). - C++ Compiler: GCC 14+ or GCC 15+ with C++26 and
-fcontractssupport. - Build System: CMake >= 3.25.
- System Libraries:
liburing-dev(>= 2.2)libssl-dev(OpenSSL 3.x)libgtest-dev(Google Test, for unit tests)pkg-configllhttp
On Debian/Ubuntu 25.10+:
sudo apt-get update
sudo apt-get install -y g++ cmake make pkg-config libssl-dev liburing-dev libgtest-dev libpq-devgit clone --recurse-submodules https://github.com/printf033/mynet.git
cd mynetIf already cloned without submodules:
git submodule update --init --recursivemkdir -p build && cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
make -j$(nproc)| Option | Default | Description |
|---|---|---|
CMAKE_BUILD_TYPE |
Debug |
Build profile (Debug, Release, RelWithDebInfo) |
BUILD_TESTING |
ON |
Build Google Test unit test executables |
MYNET_BUILD_BENCH |
ON |
Build micro-benchmark executables (bench/bench_core) |
A multi-stage Dockerfile based on Ubuntu 25.10 is included in the root directory:
docker build -t mynet:latest .
docker run --rm -it -p 8888:8888 mynet:latest#include "error.hpp"
#include "handler.hpp"
#include "log.hpp"
#include "reactor.hpp"
using namespace mynet;
int main() {
mynet::log::init(); // Directs logs to stderr
// Instantiate a Reactor with plain socket events and an echo handler
Reactor<EventSocket<HandlerBase>> server;
if (auto res = server.runTcp("0.0.0.0", 8888); !res) {
LOG_ERROR("Server failed: {}", toString(res.error()));
return toExitCode(res.error());
}
return 0;
}#include "error.hpp"
#include "handler.hpp"
#include "log.hpp"
#include "proactor.hpp"
using namespace mynet;
int main() {
mynet::log::init();
// Instantiate a Proactor using io_uring multishot accept & multishot recv
Proactor<EventSocket<HandlerBase>> server;
// Parameters: IP, Port, backlog, SQ entries, CQ entries, max accept ops, session pool, buf ring count, buf size
if (auto res = server.runTcp("0.0.0.0", 8888, 511, 512, 1024, 256, 512, 1024, 4096); !res) {
LOG_ERROR("Proactor failed: {}", toString(res.error()));
return toExitCode(res.error());
}
return 0;
}#include "error.hpp"
#include "handler.hpp"
#include "multiplexer.hpp"
#include "reactor.hpp"
using namespace mynet;
int main() {
mynet::log::init();
// Stack: Reactor -> Socket -> Multiplexer (HTTP/2 framing) -> Trace Echo Handler
Reactor<EventSocket<Multiplexer<HandlerBase>>> server;
if (auto res = server.runTcp("0.0.0.0", 8888); !res) {
LOG_ERROR("Multiplexer server failed: {}", toString(res.error()));
return toExitCode(res.error());
}
return 0;
}#include "error.hpp"
#include "handler.hpp"
#include "client.hpp"
using namespace mynet;
int main(int argc, char* argv[]) {
mynet::log::init();
Client<EventSocket<HandlerBase>> client;
// Connects to target server, streams stdin -> server and server -> stdout
if (auto res = client.runTcp("127.0.0.1", 8888); !res) {
LOG_ERROR("Client error: {}", toString(res.error()));
return toExitCode(res.error());
}
return 0;
}Usage with standard Linux utilities:
# Send an HTTP request and inspect output
printf "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n" | ./build/client_tcp 127.0.0.1 8888mynet/
├── CMakeLists.txt # Top-level CMake configuration (C++26 standard, contracts)
├── Dockerfile # Multi-stage containerized build configuration
├── LICENSE # MIT License
├── README.md # English Documentation
├── README_zh.md # Chinese Documentation
│
├── src/ # Core Header-Only Implementation
│ ├── CMakeLists.txt # Header-only interface library definition (INTERFACE mynet)
│ ├── concepts.hpp # C++20/C++26 Concept constraints
│ ├── error.hpp # Strongly-typed Err & IoEnd definitions
│ ├── log.hpp # Logging facade (mylog -> stderr)
│ ├── buffer.hpp # Low-allocation auto-compacting byte buffer
│ ├── objectPool.hpp # Fixed-capacity stable-pointer object pool
│ ├── timer.hpp # Lazy-cancellation binary min-heap timer queue
│ ├── loop.hpp # epoll EventLoop with eventfd wakeups
│ ├── event.hpp # Socket & SSL listener abstractions
│ ├── connection.hpp # Unified TCP/TLS connection engine with write queue
│ ├── datagram.hpp # Datagram transport abstraction (UDP)
│ ├── handler.hpp # HandlerBase and HandlerHttp implementations
│ ├── frame.hpp # HTTP/2 wire-compatible framing codec
│ ├── multiplexer.hpp # HTTP/2 credit-based stream multiplexer
│ ├── reactor.hpp # epoll-based Edge-Triggered Reactor engine
│ ├── proactor.hpp # io_uring-based completion Proactor engine
│ └── client.hpp # Pipeline client abstraction (stdin/stdout bridge)
│
├── samples/ # Ready-to-run Executable Samples
│ ├── reactor_tcp.cpp # Reactor TCP echo server
│ ├── reactor_ssl.cpp # Reactor TLS server
│ ├── reactor_udp.cpp # Reactor UDP echo server
│ ├── proactor_tcp.cpp # Proactor TCP server with buffer ring
│ ├── proactor_ssl.cpp # Proactor TLS server (poll-driven)
│ ├── proactor_udp.cpp # Proactor UDP server
│ ├── multiplex_tcp.cpp # Reactor stream multiplexer server
│ ├── proactor_multiplex.cpp # Proactor stream multiplexer server
│ ├── client_tcp.cpp # Stdin/stdout TCP client
│ ├── client_ssl.cpp # Stdin/stdout TLS client
│ └── client_udp.cpp # Stdin/stdout UDP client
│
├── tests/ # Unit Test Suite (Google Test + ASan)
│ ├── test_buffer.cpp
│ ├── test_concepts.cpp
│ ├── test_error.cpp
│ ├── test_frame.cpp
│ ├── test_handler.cpp
│ ├── test_multiplexer.cpp
│ ├── test_object_pool.cpp
│ └── test_timer.cpp
│
├── bench/ # Micro-benchmark Suite
│ └── bench_core.cpp # Throughput and latency micro-benchmarks
│
├── certs/ # Development self-signed certificates and keys
├── scripts/ # Maintenance & formatting scripts
└── third_party/ # External Git Submodules (mylog)
All unit tests are compiled with AddressSanitizer (ASan) and -Wall -Wextra -Wpedantic to guarantee memory safety and contract conformance:
cd build
ctest --output-on-failureOutput:
Test project /home/aaa/code/mynet/build
Start 1: test_buffer
1/8 Test #1: test_buffer ...................... Passed 0.01 sec
Start 2: test_concepts
2/8 Test #2: test_concepts .................... Passed 0.01 sec
Start 3: test_error
3/8 Test #3: test_error ....................... Passed 0.01 sec
Start 4: test_frame
4/8 Test #4: test_frame ....................... Passed 0.01 sec
Start 5: test_handler
5/8 Test #5: test_handler ..................... Passed 0.01 sec
Start 6: test_multiplexer
6/8 Test #6: test_multiplexer ................. Passed 0.02 sec
Start 7: test_object_pool
7/8 Test #7: test_object_pool ................. Passed 0.01 sec
Start 8: test_timer
8/8 Test #8: test_timer ....................... Passed 0.02 sec
100% tests passed out of 8
mynet is released under the MIT License. See LICENSE for details.