Binary Message Protocol — that's what I am calling this method. Sounds cool right? Well maybe not.
Fancy name, but it’s really just a simple method of sending data by message framing.
Problem
In real-time applications, speed and efficiency are critical. Sending large JSON payloads over WebSockets can add unnecessary overhead, making communication slower and more resource-intensive.
At the same time, the server needs a clear way to identify what kind of message it's receiving. For example, whether it's a request to join a session, an update, or some other action.
When I was building a collaboration platform with TypeScript and Golang, I ran into this exact problem: JSON was too heavy for real-time communication, and I needed a faster, lighter protocol that still allowed the server to understand the type of each message.
JSON Examples
Joining:
{
"type": "join",
"payload": { "token": "...", "doc_id": "..." }
}
`
Updating a document:
json
{
"type": "update",
"payload": { "doc_id": "abc123", "delta": [1, 0, 2, 3] }
}
// The delta could be very large
Looks fine — but that's a lot of extra characters being sent on every message.
Solution: Binary Message Protocol
A simple method where:
- First byte = message type
- Remaining bytes = payload
That’s it.
So instead of sending the verbose JSON above, we just send something like:
[2, 21, 1212, 1, 21, 2, 4, 243, 544, 4, 5, 67, 7]
Here:
-
2= message type (join) - The rest = encoded payload
Message Type Mapping
Here’s an example mapping:
| Byte | Type | Description |
|---|---|---|
| 1 | update | Document update (deltas) |
| 2 | join | Join a document session |
| 3 | leave | Leave a document session |
| 4 | ping | Keep-alive / heartbeat |
| 5 | auth | Authentication / validation |
Encoding & Decoding
This isn’t just theory — here’s how you could implement it.
Encoding in Go
go
func (c *Client) HandleNotification(msgType uint, message string) {
msgBytes := []byte(message)
payload := make([]byte, 1+len(msgBytes))
payload[0] = byte(msgType)
copy(payload[1:], msgBytes)
c.Send <- payload
}
Decoding in TypeScript
ts
function parseMessage(message: Uint8Array): {
type: number;
payload: Uint8Array;
} {
const type = message[0];
const payload = message.slice(1);
return { type, payload };
}
Sending as Binary over WebSocket
In Go, you just tell the WebSocket library that you're writing binary messages:
go
func (c *Client) WritePump() {
for msg := range c.Send {
// websocket.BinaryMessage ensures data is sent as binary
if err := c.Conn.WriteMessage(websocket.BinaryMessage, msg); err != nil {
log.Println("write error:", err)
return
}
}
}
Why Not JSON? Why Not Protobuf?
- JSON → Easy to use but heavy.
- Protobuf → Lightweight but complex.
- Binary Message Protocol → Minimal, custom, and just enough for real-time collaboration.
Closing Thoughts
This might not be advanced, and many senior engineers may find it straightforward, but I just wanted to share what I learned. Hopefully, you found it useful too.
Project
`
Top comments (0)