Introduction
September 13, 2026 · View on GitHub
Introduction
If this library helps you, please support it: Become a supporter
reliable.go is a simple packet acknowledgement system for UDP-based protocols, written in Go.
It's useful in situations where you need to know which UDP packets you sent were received by the other side.
It's a faithful port of the C library reliable (v1.4.4) to modern, idiomatic Go.
It has the following features:
- Acknowledgement when packets are received
- Packet fragmentation and reassembly
- RTT, jitter and packet loss estimates
- Duplicate packets are detected and dropped
The wire format is identical to the C library, so Go and C endpoints interoperate. This is enforced by tests: see Wire compatibility.
Usage
go get github.com/mas-bandwidth/reliable.go
reliable.go is designed to operate with your own network socket library.
First, create an endpoint on each side of the connection:
import reliable "github.com/mas-bandwidth/reliable.go"
config := reliable.DefaultConfig()
config.MaxPacketSize = 32 * 1024
config.FragmentAbove = 1200
config.MaxFragments = 32
config.FragmentSize = 1024
config.TransmitPacketFunction = transmitPacket
config.ProcessPacketFunction = processPacket
endpoint, err := reliable.NewEndpoint(&config, time)
if err != nil {
log.Fatalf("error: could not create endpoint: %v", err)
}
For example, in a client/server setup you would have one endpoint on each client, and n endpoints on the server, one for each client slot.
Next, create a function to transmit packets:
func transmitPacket(id uint64, sequence uint16, packetData []byte) {
// send packet using your own udp socket
}
And a function to process received packets:
func processPacket(id uint64, sequence uint16, packetData []byte) bool {
// read the packet here and process its contents, return false if the packet should not be acked
return true
}
To pass state to the callbacks, use a closure or a method value. The id is the Config.ID of the endpoint that fired the callback, so callbacks shared between endpoints can tell them apart — for example, one transmit function bound to one socket serving every client slot on a server.
For each packet you receive from your udp socket, call this on the endpoint that should receive it:
endpoint.ReceivePacket(packetData)
Now you can send packets through the endpoint:
endpoint.SendPacket(packetData)
And get acks like this:
for _, ack := range endpoint.Acks() {
fmt.Printf("acked packet %d\n", ack)
}
Once you process all acks, clear them:
endpoint.ClearAcks()
Before you send a packet, you can ask reliable what sequence number the sent packet will have:
sequence := endpoint.NextPacketSequence()
This way you can map acked sequence numbers to the contents of packets you sent, for example, resending unacked messages until a packet that included that message was acked.
Make sure to update each endpoint once per-frame. This keeps track of network stats like latency, jitter, packet loss and bandwidth:
endpoint.Update(time)
You can then grab stats from the endpoint:
fmt.Printf("rtt = %.1fms | jitter = %.1fms | packet loss = %.1f%%\n",
endpoint.RTTMin(),
endpoint.JitterAvgVsMinRTT(),
endpoint.PacketLoss())
See cmd/example for a complete program, and cmd/soak for a soak test you can run with go run ./cmd/soak --quiet 10000.
Caveats
reliable.go is a packet acknowledgement system, not a full messaging layer. Keep the following in mind:
-
Acks accumulate until you call
endpoint.ClearAcks, so make sure you clear acks once you have processed them each frame. If the ack buffer fills up, additional acks are dropped and an error is logged. -
Endpoints are not thread safe. Use one endpoint per-goroutine, or protect each endpoint with your own lock. The log level and printf handler are global to the process.
Differences from the C library
The port keeps the structure, behavior and wire format of the C library, with the following adaptations to Go:
reliable_endpoint_createisreliable.NewEndpointand returns an error for invalid configs instead of asserting.- There are no custom allocator hooks — the Go garbage collector manages memory, and
reliable_endpoint_free_packet/reliable_endpoint_destroyhave no equivalent. - The
void * contextparameter on the callbacks is gone — closures and method values are how state reaches callbacks in Go. The endpoint id remains. ReceivePackettreats an empty packet as invalid instead of asserting, since network input is untrusted.SendPacketpanics on an empty packet, which is a programmer error.reliable_init/reliable_termare gone — there is no library state to initialize.- Stats are float64 instead of float.
Sending and receiving packets does not allocate (fragment reassembly allocates one buffer per fragmented packet, just like the C library). Run go test -bench . to check on your hardware.
Wire compatibility
Binary compatibility with the C library is locked in by a golden transcript test that runs as part of go test, on every platform and every pull request:
- interop/transcript.c runs a deterministic scenario through the C library — 300 frames of bidirectional traffic with regular and fragmented packets, deterministic packet loss and duplication — and prints every transmitted packet as hex, every ack, and the endpoint counters.
- The output, generated from the C library pinned at the commit in interop/regenerate.sh, is committed as
testdata/c_transcript.txt.gz. TestWireCompatibility(wire_compat_test.go) runs the identical scenario through the Go port and requires byte-for-byte identical output.- A CI job rebuilds the golden from the pinned C sources on every run, so the golden itself cannot drift from the C library.
If you change anything that touches the wire format, this test fails and points at the first diverging line.
Author
The author of this library is Glenn Fiedler.
Open source libraries by the same author include: reliable, netcode, serialize, and yojimbo
If you find this software useful, please consider becoming a supporter. Thanks!
License
MBSL.
Crediting
This library is licensed under the Más Bandwidth Source License (MBSL), which is BSD 3-Clause plus one clause: products that incorporate it must include this credit in their product credits, or in their documentation:
reliable.go by Glenn Fiedler and Rowan Claude
Free to use, source open, credit required. Fair credit keeps open source honest.