Minimal, modern UDP protocol runtime — a leaner alternative to ENet.
Features • Why? • Usage • Design • Build Requirements • FetchContent • License
pulse::net::proto
is a C++23-native, zero-dependency, zero-bloat UDP protocol library with full support for reliable messaging, channels, latency measurement, and stateful sessions — all in a sane, modern API.
It does what ENet tried to do, but without the legacy cruft, broken reliability layer, or “callback hell” API.
Status: 🔬 Early alpha (v0.1.0)
- ✅ Lightweight client-server session manager over UDP
- ✅ Reliable and unreliable packet delivery
- ✅ Explicit per-channel state
- ✅ Per-session RTT measurement (ping/pong)
- ✅ Packet reordering and receive window handling
- ✅ No fragmentation, no TCP emulation nonsense
- ✅ Pure C++23 (
std::expected
, no exceptions, no macros) - ✅ Easily mockable interfaces (
Session
,Protocol
) - ✅ Pluggable logger + metrics
- ✅ No dynamic memory overhead from fragmentation or excessive buffering
- ✅ Fully deterministic, testable integration model
Because networking code has been rotting in C++ since before Steam existed. ENet was fine in 2004. It's not 2004.
This library is for building real-time game servers, low-latency control loops, or anything else where UDP is required and you still want sane abstractions.
It doesn’t reinvent IP. It doesn’t try to guarantee delivery of your dreams. But it does give you:
- Reliability with proper ACKs
- Session state machines
- No-crap packet headers
- Predictable flow
If you want a TLS-based RPC layer, you’re lost.
#include <pulse/net/proto/proto.h>
#include <pulse/net/udp/udp.h>
using namespace pulse::net;
int main() {
auto serverAddr = udp::Addr("127.0.0.1", 40000);
auto listenResult = udp::Listen(serverAddr);
if (!listenResult) return 1;
auto& socket = **listenResult;
auto proto = proto::CreateProtocol(
socket,
[](proto::Session& session, uint8_t channel, proto::BufferView view) {
// Process incoming message
},
[](const udp::Addr& disconnected) {
// Handle disconnect
}
);
while (true) {
proto->tick(); // recv, dispatch, flush
}
}
session.sendReliable(1, proto::BufferView(data, len));
session.sendUnreliable(2, proto::BufferView(data, len));
See drd.md
for the full design doc. Here's the short version:
Feature | Description |
---|---|
Sessions | One per client IP:port, tracked automatically |
Reliable delivery | Per-channel resend queues, ACK-based delivery |
Unreliable fire-and-forget | Zero-state, minimal header |
Channels | Up to 256, independently managed |
Ping/Pong | RTT measurement, smoothed |
No Fragmentation | Max packet size is hard-capped at 1200 bytes |
Timeouts | Idle sessions are culled automatically |
Protocol versioning | Handshake has a version field for future use |
Logging | Pluggable logger interface |
Metrics | Pluggable metrics interface |
Zero exceptions | Only constructors throw — everything else uses std::expected |
You want handshakes? It has those. You want encryption? Go layer TLS on top.
- C++23
- CMake ≥ 3.15
- pulse::net::udp (see here)
include(FetchContent)
FetchContent_Declare(
pulsenet_proto
GIT_REPOSITORY https://git.pulsenet.dev/pulse/proto
GIT_TAG main
)
FetchContent_MakeAvailable(pulsenet_proto)
target_link_libraries(your_target PRIVATE pulsenet::proto)
Note: Depends FetchContent will pull the pulse::net::udp
library as well from the tip of main
branch.
AGPLv3.
Yes, it's intentional.
Want to use this in a proprietary product? Buy a commercial license or go write your own UDP protocol stack.
- ❌ NAT traversal
- ❌ TLS or encryption
- ❌ Arbitrary TCP-over-UDP framing
- ❌ Fragmentation / jumbo packet support
- ❌ Unreliable unordered delivery
- ❌ Handholding
Includes standalone integration tests:
mkdir build && cd build
cmake .. -DPULSENET_PROTO_STANDALONE_BUILD=ON
cmake --build .
./pulsenet_proto_test
pulse::net::proto v0.1.0