Building a Pure-Go Socket.IO v4 Server in Go
What if you could build a Socket.IO-compatible server in Go without depending on an existing WebSocket or Socket.IO library?
That question led me to build gsocketio.
🔗 Live demo: https://gsocketio.vercel.app/
🔗 GitHub: https://github.com/shishir1290/gsocketio
What is gsocketio?
gsocketio is a pure-Go Socket.IO v4 server implementation.
The goal is simple:
Implement the Socket.IO and Engine.IO protocol directly in Go using the standard library.
The project intentionally avoids third-party Go networking packages such as Gorilla WebSocket and other WebSocket/Socket.IO implementations.
The result is a lightweight implementation where the protocol, transport layer, packet handling, rooms, and WebSocket framing are all part of the project.
Why build another Socket.IO server?
Go already has excellent networking capabilities, but building a Socket.IO-compatible server is an interesting engineering challenge.
Socket.IO is more than just WebSocket.
A compatible implementation needs to deal with things such as:
- Engine.IO handshakes
- Socket.IO packets
- WebSocket framing
- HTTP long polling
- Polling → WebSocket upgrades
- Namespaces
- Rooms
- Broadcasting
- ACK packets
- Binary events
- Authentication payloads
- Connection lifecycle
- CORS
- Concurrent connections
Building these pieces from the protocol level helped me understand how Socket.IO actually works underneath the client API.
Current protocol support
gsocketio currently targets:
- Socket.IO v5 protocol
- Socket.IO v4 clients
- Engine.IO v4
- WebSocket transport
- HTTP long polling
- Polling → WebSocket upgrade
- Namespaces
- Rooms
- Broadcasting
- ACKs
- Binary events
- Binary ACKs
- Connection context
- CORS / preflight
- Concurrent server operation
The repository also contains integration tests using a real Socket.IO v4 client.
Zero third-party Go dependencies
One of the design goals was keeping the core implementation dependency-free.
The go.mod intentionally contains only the module declaration and Go version.
module github.com/shishir1290/gsocketio
go 1.22.2
That means the transport and protocol implementation are handled inside the project itself.
Starting a server
The basic server can be created with:
srv := sio.New(nil)
srv.OnConnect("/", func(c sio.Conn) error {
c.Join("lobby")
return c.Emit("welcome", "Hello from Go!")
})
Then expose the Socket.IO endpoint through net/http:
http.Handle("/socket.io/", srv)
log.Fatal(http.ListenAndServe(":8080", nil))
That's enough to start experimenting with a Socket.IO-compatible Go server.
Connecting from a Socket.IO client
A standard Socket.IO v4 client can connect to the server:
import { io } from "socket.io-client";
const socket = io("http://localhost:8080");
socket.on("connect", () => {
console.log("connected:", socket.id);
});
socket.on("welcome", message => {
console.log(message);
});
socket.emit("chat", "Hello!");
You can also explicitly test the different Engine.IO transports:
io("http://localhost:8080", {
transports: ["websocket"]
});
or:
io("http://localhost:8080", {
transports: ["polling"]
});
or allow the normal polling → WebSocket upgrade:
io("http://localhost:8080", {
transports: ["polling", "websocket"]
});
Rooms and broadcasting
Rooms are another important part of Socket.IO.
For example:
c.Join("lobby")
Then broadcast an event to everyone in the room:
srv.ToRoom(
"/",
"lobby",
"chat",
c,
"Hello everyone!",
)
You can also broadcast to the entire namespace:
srv.ToNamespace(
"/",
"announcement",
"Server maintenance soon",
)
This makes the implementation useful for applications such as:
- Chat applications
- Real-time dashboards
- Multiplayer experiments
- Notifications
- Collaboration tools
- Live monitoring systems
Namespaces
gsocketio also supports Socket.IO namespaces.
For example:
srv.OnConnect("/", func(c sio.Conn) error {
return nil
})
srv.OnConnect("/admin", func(c sio.Conn) error {
return nil
})
Events can then be registered for a specific namespace:
srv.OnEvent("/admin", "status", func(
c sio.Conn,
args []json.RawMessage,
) {
_ = c.Emit("status", "ok")
})
Authentication context
Socket.IO authentication data can be accessed from the connection context.
For example:
srv.OnConnect("/", func(c sio.Conn) error {
auth := c.Context()
log.Printf("auth: %#v", auth)
return nil
})
Application-specific data can also be attached to a connection:
c.SetContext(map[string]string{
"user": "123",
})
This gives applications a place to associate authenticated users or other connection-specific information with a Socket.IO connection.
Binary events
The implementation also supports Socket.IO binary events.
For example:
srv.OnBinaryEvent("/", "upload", func(
c sio.Conn,
args []interface{},
id *int,
) {
_ = c.Emit("upload-complete", "ok")
})
Binary Socket.IO packets use the attachment/placeholder mechanism defined by the Socket.IO protocol.
Testing the implementation
I wanted the project to be more than just an implementation that "looks like" Socket.IO.
The repository includes tests covering different parts of the protocol and server.
Some of the validation includes:
go test ./...
Static analysis:
go vet ./...
Race detection:
go test -race ./...
Formatting:
test -z "$(gofmt -l .)"
The CI setup also tests the implementation against a real Socket.IO v4 client and checks:
- WebSocket transport
- Polling transport
- Polling → WebSocket upgrade
- Authentication payloads
- Events
- Next.js integration
What I learned
The most interesting part of this project wasn't writing an API like:
socket.emit("message", data);
The interesting part was understanding what happens underneath it.
There are multiple protocol layers involved:
Socket.IO
↓
Engine.IO
↓
HTTP Polling / WebSocket
↓
TCP
A Socket.IO server therefore needs to correctly handle both the Socket.IO protocol and the Engine.IO transport layer.
Implementing these pieces directly made the protocol much easier to understand.
Project structure
The repository is organized around the protocol components:
gsocketio/
├── gsocketio.go
├── go.mod
├── transport/
│ ├── transport.go
│ └── transport_test.go
├── packet/
│ ├── packet.go
│ └── packet_test.go
├── rooms/
│ ├── rooms.go
│ └── rooms_test.go
├── server/
│ └── server.go
├── logger/
│ └── logger.go
├── examples/
│ ├── basic/
│ └── chat/
└── tests/
├── protocol_v4_test.go
└── server_integration_test.go
Try it yourself
Install it with:
go get github.com/shishir1290/gsocketio@latest
Then:
import sio "github.com/shishir1290/gsocketio"
The project is open source, so you can inspect the implementation, run the tests, experiment with the protocol, and build your own real-time Go applications on top of it.
Final thoughts
gsocketio started as an experiment to understand how Socket.IO works at a lower level.
It has grown into a working pure-Go implementation supporting many of the features needed by real Socket.IO clients.
There is still plenty of room for improvement, optimization, compatibility testing, and additional protocol edge cases.
If you're interested in Go, WebSockets, Socket.IO, Engine.IO, networking protocols, or real-time systems, I'd love for you to check it out.
⭐ GitHub: https://github.com/shishir1290/gsocketio
🌐 Live demo: https://gsocketio.vercel.app/
If you find a protocol incompatibility or have an interesting test case, feel free to open an issue or contribute to the project.

Top comments (0)