socketio

package module
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 19, 2026 License: MIT Imports: 14 Imported by: 0

README

socket.io

Go Test Go Lint Go Vuln Web Unit Web E2E Go Reference Go Report Card Go Version Release Last Commit Code Size PRs Welcome Docs

Node's Socket.IO, for Go.

A dependency-free Go port of the Socket.IO server. It implements the real wire protocol — Engine.IO v4 (HTTP long-polling and WebSocket, including the polling→WebSocket upgrade) and Socket.IO v5 (namespaces, rooms, events, and acknowledgements) — so it interoperates with standard Socket.IO clients. The WebSocket transport is implemented from scratch on net/http; there are no third-party dependencies.

package main

import (
	"net/http"

	socketio "github.com/malcolmston/socketio"
)

func main() {
	io := socketio.New()

	io.OnConnection(func(s *socketio.Socket) {
		s.Join("general")

		s.On("chat", func(args []any) []any {
			io.To("general").Emit("chat", args...) // broadcast to the room
			return nil
		})

		s.On("ping", func(args []any) []any {
			return []any{"pong"} // acknowledgement reply
		})
	})

	http.Handle(socketio.DefaultPath, io)
	http.ListenAndServe(":3000", nil)
}

Install

go get github.com/malcolmston/socketio

Design

The implementation is layered exactly like the Node original:

Layer Package Responsibility
Engine.IO codec engineio packet + polling-payload encode/decode
WebSocket internal/ws RFC 6455 server (handshake, framing, control frames)
Socket.IO codec socketio (protocol.go) CONNECT/EVENT/ACK/... packet encode/decode
Server socketio transports, sessions, namespaces, rooms, sockets

Both codecs are pure and independently unit-tested; the server is exercised end-to-end over both transports.

The API

Server
io := socketio.New()                 // default options
io := socketio.New(socketio.Options{ // or configure
	PingInterval: 25 * time.Second,
	PingTimeout:  20 * time.Second,
})

io.OnConnection(func(s *socketio.Socket) { ... }) // default namespace
io.Of("/admin").OnConnection(func(s *socketio.Socket) { ... })
io.Emit("event", data)               // broadcast to everyone
io.To("room").Emit("event", data)    // broadcast to a room

An *socketio.Server is an http.Handler; mount it at socketio.DefaultPath (/socket.io/).

Socket
s.ID()                               // unique socket id
s.Auth()                             // CONNECT auth payload
s.On("event", handler)               // register a handler
s.Emit("event", args...)             // send to this client
s.EmitWithAck("event", cb, args...)  // send and await the client's ack
s.Join("room"); s.Leave("room")      // room membership
s.To("room").Emit("event", data)     // broadcast to a room, excluding self
s.Broadcast().Emit("event", data)    // everyone except this socket
s.OnDisconnect(func(reason string){})
s.Disconnect(true)                   // force-disconnect
Per-socket data (session-like state)

Attach arbitrary state to a socket for the lifetime of the connection — the equivalent of socket.data:

io.OnConnection(func(s *socketio.Socket) {
	s.Set("user", currentUser).Set("tenant", "acme")

	s.On("action", func(args []any) []any {
		user := s.GetString("user")
		v, _ := s.Get("tenant")
		_ = v
		return nil
	})
})

s.Set, s.Get, s.GetString, s.Delete, and s.Data() manage the store; it is concurrency-safe.

Handlers & acknowledgements

An event handler receives the event arguments and returns an optional acknowledgement payload:

s.On("ping", func(args []any) []any {
	return []any{"pong"} // sent back only if the client requested an ack
})

Return nil for no acknowledgement.

Rooms & broadcasting
s.Join("room42")
io.To("room42").Emit("news", payload)          // all members
s.To("room42").Emit("news", payload)           // members except the sender
io.Of("/admin").To("ops").Emit("alert", data)  // per-namespace

Connection middleware

Gate connections with Use (the equivalent of io.use). Calling next(err) rejects the connection with a connect_error:

io.Use(func(s *socketio.Socket, next func(error)) {
	if s.Auth() == nil {
		next(errors.New("unauthorized"))
		return
	}
	next(nil)
})
io.Of("/admin").Use(adminOnly) // per-namespace middleware

Go client

A Go client ships in client, built on the same transport stack:

import "github.com/malcolmston/socketio/client"

c, _ := client.Dial("http://localhost:3000")
defer c.Close()

c.On("news", func(args []any) []any { fmt.Println(args); return nil })
c.Emit("hello", "world")

reply, _ := c.EmitWithAck("ping", 5*time.Second) // blocks for the ack

The client supports binary payloads and automatic reconnection:

c, _ := client.Dial(url, client.Options{Reconnection: true})
c.On("reconnect", func(args []any) []any { log.Println("reconnected"); return nil })

The server side can likewise request an acknowledgement and block for it with socket.EmitAck(event, timeout, args...). Server.Close() disconnects all sessions.

Using with express (or any net/http handler)

Attach the Socket.IO server to an existing HTTP server, letting express (or an http.ServeMux) handle everything else — exactly like new Server(httpServer) in Node:

app := express.New()                       // your routes
app.Get("/api/hello", helloHandler)

io := socketio.New()                       // your socket handlers
io.OnConnection(func(s *socketio.Socket) { ... })

// io.Handler intercepts /socket.io/ and delegates the rest to app.
http.ListenAndServe(":3000", io.Handler(app))

io.Handler(next) takes any http.Handler; pass nil to 404 non-Socket.IO requests. You can also mount it the other way — as express middleware — with app.Use("/socket.io", express.WrapHandler(io)), or register it on a mux with io.Attach(mux).

Binary events

Emit and receive []byte payloads; they are carried as Socket.IO binary attachments (native WebSocket binary frames, or base64 over polling):

s.Emit("blob", []byte{0x00, 0x01, 0x02})
s.On("upload", func(args []any) []any {
	data := args[0].([]byte)
	return []any{len(data)}
})

Server-wide operations

io.FetchSockets()                 // all connected sockets
io.SocketsJoin("room")            // make every socket join a room
io.SocketsLeave("room")
io.DisconnectSockets(true)        // disconnect everyone
io.ServerSideEmit("event", data)  // server-to-server event (single-node: local)

Rooms are stored behind a pluggable Adapter (ns.SetAdapter); the default is in-process. Broadcast flags io.To("r").Volatile().Compress(false).Emit(...) are supported (advisory on this single-node implementation).

Scaling out with Redis

For multiple server instances, install a Broadcaster so broadcasts fan out across nodes. The redis subpackage provides one, speaking the Redis pub/sub protocol directly (no third-party client):

import "github.com/malcolmston/socketio/redis"

bc, _ := redis.New(redis.Options{Addr: "localhost:6379", Channel: "socket.io"})
io.SetBroadcaster(bc)

With a broadcaster installed, io.To(room).Emit(...) on any node is delivered to matching sockets on every node. Broadcaster is a small interface (Publish / OnMessage / Close), so any pub/sub transport can back it.

Transports

  • HTTP long-polling — the default the JS client opens with; fully supported (handshake, GET poll, POST).
  • WebSocket — implemented from scratch (RFC 6455). Clients may connect directly (transports: ['websocket']) or start on polling and upgrade; the Engine.IO 2probe/3probe/5 upgrade handshake is handled.

Status & scope

Supported: Engine.IO v4 framing, both transports + upgrade, heartbeat, the Socket.IO v5 text protocol (CONNECT, DISCONNECT, EVENT, ACK, CONNECT_ERROR), multiple namespaces, rooms, broadcasting, and acknowledgements in both directions. Binary attachments (BINARY_EVENT/BINARY_ACK) are parsed but the convenience API focuses on JSON payloads.

Example

go run ./examples/chat

License

MIT

Documentation

Overview

Package socketio is a pure-Go port of the Node.js Socket.IO server. It implements the Engine.IO v4 transport layer (HTTP long-polling and WebSocket, with the polling→websocket upgrade) and the Socket.IO v5 protocol (namespaces, rooms, events, and acknowledgements), exposing an API that mirrors the original JavaScript library so that idioms such as io.on("connection"), socket.join(room), and io.to(room).emit(...) translate almost line for line:

io := socketio.New()
io.OnConnection(func(s *socketio.Socket) {
	s.On("chat", func(args []any) []any {
		io.To("room1").Emit("chat", args...)
		return nil
	})
	s.Join("room1")
})
http.Handle("/socket.io/", io)
http.ListenAndServe(":3000", nil)

Reach for this package when you want real-time, bidirectional, event-based communication between a Go backend and Socket.IO clients (browsers, mobile apps, or the companion client package) without pulling in cgo or third-party dependencies — the entire stack, down to the RFC 6455 WebSocket framing, is built on the standard library. A *Server is an http.Handler, so it mounts on any net/http server or router: use ServeHTTP directly, Attach it to a *http.ServeMux, or wrap an existing handler with Handler to coexist with a REST/Express-style application on the same port.

Internally each connected client owns one Engine.IO session (a conn), identified by a session id (sid). A session begins over HTTP long-polling and is transparently upgraded to WebSocket via the Engine.IO probe handshake; the engineio subpackage encodes the transport frames and the polling payload, while this package encodes the Socket.IO layer on top — CONNECT, EVENT, ACK, DISCONNECT, and their binary variants. Events carry JSON arguments; any []byte in a payload is transmitted out-of-band as a BINARY_EVENT with placeholder markers (see binary.go). A single session may be attached to several Namespaces at once, each of which multiplexes its own sockets, rooms, and connection middleware over the shared transport.

The concurrency and delivery semantics follow from that design. The server sends periodic heartbeat pings and disconnects a session whose pong is overdue by more than PingInterval+PingTimeout. Inbound events are dispatched on their own goroutine so a handler may block on an acknowledgement without stalling the read loop, which means handlers for the same socket can run concurrently and must guard any shared state; ordering of delivery to the wire is preserved, but ordering between independently dispatched handlers is not. Socket.Emit targets one client and returns an error, whereas the broadcast forms (Server.Emit, Namespace.Emit, and BroadcastOperator.Emit) fan out to many recipients and deliberately do not surface a per-socket error. Rooms are managed by a pluggable Adapter; the default keeps all membership in process.

Parity with the Node reference implementation is close but not total. The wire protocols are compatible, so this server interoperates with the official JavaScript client and this module's client package. Room broadcasting, acknowledgements, connection middleware (Use), per-socket data (Set/Get), and multi-node scale-out (via SetBroadcaster and the redis subpackage) are all supported. Differences reflect Go idioms and scope: handlers use typed func([]any) []any signatures rather than variadic JS callbacks, there is no built-in adapter persistence beyond what a Broadcaster provides, and features tied to the Node runtime (such as the admin UI or the msgpack parser) are out of scope. See COMPATIBILITY.md in the repository for the authoritative matrix.

Index

Examples

Constants

View Source
const DefaultPath = "/socket.io/"

DefaultPath is the HTTP path Socket.IO serves from.

View Source
const ProtocolVersion = 5

ProtocolVersion is the Socket.IO protocol revision implemented here (v5, which rides on Engine.IO v4).

Variables

View Source
var ErrEmptyEvent = errors.New("socketio: empty event name")

ErrEmptyEvent indicates an empty event name.

View Source
var ErrInvalidPacket = errors.New("socketio: invalid packet")

ErrInvalidPacket indicates a malformed Socket.IO packet.

View Source
var ErrReservedEvent = errors.New("socketio: reserved event name")

ErrReservedEvent indicates an attempt to use a reserved event name for an application event.

Functions

func IsReservedEvent added in v0.3.0

func IsReservedEvent(name string) bool

IsReservedEvent reports whether name is a Socket.IO reserved event name (such as "connect", "disconnect", or "connect_error"). Reserved names are emitted by the library itself and must not be used for application events, mirroring the RESERVED_EVENTS guard in the JavaScript server.

func Reconstruct

func Reconstruct(data any, buffers [][]byte) any

Reconstruct walks a payload replacing {"_placeholder":true,"num":N} markers with the matching binary buffer. It is exported for client implementations that reassemble incoming binary packets.

func ReservedEvents added in v0.3.0

func ReservedEvents() []string

ReservedEvents returns a sorted copy of the reserved event names, useful for documentation and tooling.

func ValidateEventName added in v0.3.0

func ValidateEventName(name string) error

ValidateEventName checks that name is usable as an application event name: it must be non-empty and must not collide with a reserved Socket.IO event. It returns ErrEmptyEvent or ErrReservedEvent on failure, and nil when the name is safe to emit.

Types

type Adapter

type Adapter interface {
	// Add registers a socket in the namespace.
	Add(socketID string, s *Socket)
	// Remove deletes a socket and drops it from every room.
	Remove(socketID string)
	// Join adds a socket to a room.
	Join(socketID, room string)
	// Leave removes a socket from a room.
	Leave(socketID, room string)
	// SocketsInRoom returns the sockets that are members of a room.
	SocketsInRoom(room string) []*Socket
	// AllSockets returns every socket in the namespace.
	AllSockets() []*Socket
	// Get returns a socket by id.
	Get(socketID string) (*Socket, bool)
}

Adapter stores which sockets belong to a namespace and its rooms. The default implementation keeps everything in process; supplying a custom Adapter (e.g. backed by Redis) is the extension point for scaling a namespace across multiple server instances.

type BroadcastOperator

type BroadcastOperator struct {
	// contains filtered or unexported fields
}

BroadcastOperator emits events to a filtered set of sockets in a namespace — optionally scoped to one or more rooms and excluding specific sockets. It is returned by Namespace.To, Server.To, and Socket.To, and is the equivalent of io.to(room).emit(...).

func (*BroadcastOperator) Compress

func (b *BroadcastOperator) Compress(on bool) *BroadcastOperator

Compress sets whether the payload should be compressed by the transport. It is advisory in this implementation.

func (*BroadcastOperator) Emit

func (b *BroadcastOperator) Emit(event string, args ...any)

Emit sends an event to every socket matched by the operator. When a cluster Broadcaster is installed, the broadcast is published to all nodes (which each deliver it to their local sockets); otherwise it is delivered locally.

func (*BroadcastOperator) Except

func (b *BroadcastOperator) Except(socketID string) *BroadcastOperator

Except excludes a socket id from the broadcast.

func (*BroadcastOperator) ExceptRoom added in v0.3.0

func (b *BroadcastOperator) ExceptRoom(rooms ...string) *BroadcastOperator

ExceptRoom excludes every socket that is a member of any of the given rooms from the broadcast — the equivalent of io.except(room). Unlike Except, which excludes an individual socket id, ExceptRoom excludes whole rooms.

func (*BroadcastOperator) In

In is an alias for To, matching socket.io's io.in(room).

func (*BroadcastOperator) Local added in v0.3.0

Local restricts the broadcast to sockets connected to this node, suppressing cluster fan-out even when a Broadcaster is installed — the equivalent of io.local.emit(...). It is a no-op on a single-node deployment.

func (*BroadcastOperator) Timeout added in v0.3.0

Timeout records an acknowledgement timeout for the broadcast, matching io.timeout(ms).emit(...). It is stored for callers that aggregate acks; the convenience Emit itself is fire-and-forget.

func (*BroadcastOperator) To

To narrows the broadcast to an additional room.

func (*BroadcastOperator) Volatile

func (b *BroadcastOperator) Volatile() *BroadcastOperator

Volatile marks the broadcast as volatile: messages that cannot be delivered immediately (e.g. to a client mid-reconnect) may be dropped. On this single-node, buffered implementation it is advisory.

type BroadcastTarget

type BroadcastTarget interface {
	Emitter
	RoomTargeter
}

BroadcastTarget is the combination satisfied by the room-addressable emitters (server, namespace, and broadcast operator): they can both narrow to a room and emit to the resulting set.

type Broadcaster

type Broadcaster interface {
	// Publish sends a serialized broadcast to all instances (including this
	// one — pub/sub echoes to the publisher).
	Publish(data []byte) error
	// OnMessage registers the handler invoked for every received broadcast.
	OnMessage(func(data []byte))
	// Close shuts the broadcaster down.
	Close() error
}

Broadcaster fans broadcasts out to other server instances. Installing one (Server.SetBroadcaster) turns a single-node server into a cluster member: a broadcast is published once and each node delivers it to its own local sockets. The reference implementation is the Redis adapter in the redis subpackage, but any pub/sub transport can implement this interface.

type Decoder added in v0.3.0

type Decoder struct {
	// contains filtered or unexported fields
}

Decoder reassembles a packet from the stream of transport frames produced by an Encoder. It is the Go equivalent of socket.io-parser's Decoder and is stateful: feed it frames with Add in arrival order and it returns a completed packet once all of a packet's frames have been seen. A single Decoder handles one packet at a time and must not be used concurrently.

func NewDecoder added in v0.3.0

func NewDecoder() *Decoder

NewDecoder returns an empty Decoder ready to accept frames.

func (*Decoder) Add added in v0.3.0

func (d *Decoder) Add(frame any) (*Packet, error)

Add feeds one transport frame to the decoder. The frame must be a string (a text frame, i.e. a Socket.IO text packet) or a []byte (a binary attachment frame). Add returns a non-nil packet when the frame completes a packet, or (nil, nil) when more frames are required (a binary header awaiting its attachments). A malformed frame, or a binary attachment arriving with no header pending, returns ErrInvalidPacket.

Reassembled binary packets keep their BinaryEvent/BinaryAck type, with every {"_placeholder":true,"num":N} marker in the payload replaced by the matching buffer — mirroring socket.io-parser, whose consumers treat BINARY_EVENT the same as EVENT (use EventName and Args as usual).

func (*Decoder) Pending added in v0.3.0

func (d *Decoder) Pending() bool

Pending reports whether the decoder is mid-packet, waiting for binary attachment frames to complete the packet whose header it already received.

func (*Decoder) Reset added in v0.3.0

func (d *Decoder) Reset()

Reset discards any partially-decoded packet, returning the decoder to its empty state.

type Emitter

type Emitter interface {
	Emit(event string, args ...any)
}

Emitter is anything that can broadcast an event to a set of sockets without a per-socket error — the server, a namespace, and a broadcast operator all fan an event out to many recipients, so a delivery error to any single socket is not surfaced. (Socket.Emit, which targets one client, deliberately returns an error and is therefore not an Emitter.)

type Encoder added in v0.3.0

type Encoder struct{}

Encoder encodes Socket.IO packets into the sequence of transport frames they occupy. It is the Go equivalent of socket.io-parser's Encoder: a plain, stateless value that is safe to share across goroutines.

func NewEncoder added in v0.3.0

func NewEncoder() *Encoder

NewEncoder returns a ready-to-use Encoder.

func (*Encoder) Encode added in v0.3.0

func (e *Encoder) Encode(p Packet) ([]any, error)

Encode renders a packet to the ordered frames it occupies on the wire. The first element is always the packet's text form (a Go string); when the packet carries []byte values the type is promoted to its binary variant, the text header declares the attachment count, and each following element is a raw binary buffer ([]byte). A caller writes element 0 as a text frame and every subsequent element as a binary frame, in order.

type EventHandler

type EventHandler func(args []any) []any

EventHandler handles an inbound event. It receives the event arguments and may return a non-nil slice to acknowledge the event (sent back to the client when the event requested an ack).

type Handshake added in v0.3.0

type Handshake struct {
	// Headers is the set of HTTP request headers.
	Headers http.Header
	// Time is when the handshake was processed.
	Time time.Time
	// Address is the remote network address the connection originated from
	// (honoring X-Forwarded-For when present).
	Address string
	// XDomain reports whether the request carried an Origin header (a
	// cross-origin, browser-initiated request).
	XDomain bool
	// Secure reports whether the request arrived over TLS (https/wss).
	Secure bool
	// Issued is the handshake time in Unix milliseconds, matching the JS
	// handshake.issued field.
	Issued int64
	// URL is the request URI (path and query) of the handshake request.
	URL string
	// Query holds the parsed query-string parameters.
	Query url.Values
	// Auth is the authentication payload the client supplied with CONNECT.
	Auth any
}

Handshake describes the details of the HTTP request that established a connection — the Go equivalent of Socket.IO's socket.handshake object. It is captured once, at connection time, and never changes for the life of the socket. Middleware and connection handlers inspect it to authorize or annotate a connection (origin, forwarded address, query string, auth payload).

func NewHandshake added in v0.3.0

func NewHandshake(r *http.Request, auth any) *Handshake

NewHandshake builds a Handshake from an incoming HTTP request and the auth payload sent with the Socket.IO CONNECT packet. The remote address prefers the first entry of an X-Forwarded-For header (for connections behind a proxy) and otherwise falls back to the request's RemoteAddr with any port stripped.

func (*Handshake) Get added in v0.3.0

func (h *Handshake) Get(header string) string

Get returns the first value of a request header (case-insensitive), or "" if absent — a convenience over reaching into Headers directly.

func (*Handshake) Param added in v0.3.0

func (h *Handshake) Param(key string) string

Param returns the first value of a query-string parameter, or "" if absent.

type Namespace

type Namespace struct {
	// contains filtered or unexported fields
}

Namespace is a communication channel that partitions a Socket.IO server. Each namespace has its own set of connected sockets, rooms, and connection handlers. The default namespace is "/".

func (*Namespace) DisconnectSockets

func (ns *Namespace) DisconnectSockets(closeTransport bool)

DisconnectSockets disconnects every socket in the namespace.

func (*Namespace) Emit

func (ns *Namespace) Emit(event string, args ...any)

Emit broadcasts an event to every socket in the namespace.

func (*Namespace) Except added in v0.3.0

func (ns *Namespace) Except(room string) *BroadcastOperator

Except returns a broadcast operator over the namespace that excludes every socket in the given room — the equivalent of io.except(room).

func (*Namespace) FetchSockets

func (ns *Namespace) FetchSockets() []*Socket

FetchSockets returns all sockets in the namespace (alias for Sockets, matching io.fetchSockets()).

func (*Namespace) In added in v0.3.0

func (ns *Namespace) In(room string) *BroadcastOperator

In is an alias for To, scoping a broadcast to a room within this namespace — the equivalent of io.in(room).

func (*Namespace) Local added in v0.3.0

func (ns *Namespace) Local() *BroadcastOperator

Local returns a broadcast operator over the namespace restricted to this node — the equivalent of io.local.

func (*Namespace) Name

func (ns *Namespace) Name() string

Name returns the namespace name (e.g. "/" or "/admin").

func (*Namespace) OnConnection

func (ns *Namespace) OnConnection(fn func(*Socket)) *Namespace

OnConnection registers a handler invoked for each new socket that connects to this namespace.

func (*Namespace) SetAdapter

func (ns *Namespace) SetAdapter(a Adapter) *Namespace

SetAdapter replaces the namespace's room adapter (e.g. with a Redis-backed one for multi-node scale-out). Call it before any sockets connect.

func (*Namespace) Sockets

func (ns *Namespace) Sockets() []*Socket

Sockets returns all sockets currently connected to the namespace.

func (*Namespace) SocketsInRoom

func (ns *Namespace) SocketsInRoom(room string) []*Socket

SocketsInRoom returns the sockets that are members of a room.

func (*Namespace) SocketsJoin

func (ns *Namespace) SocketsJoin(rooms ...string)

SocketsJoin makes every socket in the namespace join the given rooms.

func (*Namespace) SocketsLeave

func (ns *Namespace) SocketsLeave(rooms ...string)

SocketsLeave makes every socket in the namespace leave the given rooms.

func (*Namespace) To

func (ns *Namespace) To(room string) *BroadcastOperator

To returns a broadcast operator scoped to a room within this namespace.

func (*Namespace) Use

func (ns *Namespace) Use(fn func(socket *Socket, next func(err error))) *Namespace

Use registers connection middleware for the namespace. Each middleware runs for every incoming connection before the connection handler; calling next with a non-nil error rejects the connection with a CONNECT_ERROR carrying the error's message — the equivalent of io.use((socket, next) => ...).

type Options

type Options struct {
	// Path is the HTTP path the server handles (default "/socket.io/").
	Path string
	// PingInterval is how often the server sends heartbeat pings.
	PingInterval time.Duration
	// PingTimeout is how long the server waits for a pong before disconnecting.
	PingTimeout time.Duration
	// MaxPayload advertises the maximum HTTP payload size to clients.
	MaxPayload int
	// CheckOrigin, if set, authorizes cross-origin requests. When nil, all
	// origins are allowed.
	CheckOrigin func(r *http.Request) bool
}

Options configures a Server.

type Packet

type Packet struct {
	// Type is the packet's Socket.IO type (Connect, Event, Ack, ...).
	Type PacketType
	// Namespace the packet targets; defaults to "/".
	Namespace string
	// ID is the acknowledgement id when the packet requests or answers an ack;
	// nil otherwise.
	ID *uint64
	// Data is the decoded JSON payload: an array for Event/Ack ([name, args...]
	// or [args...]) and an object for Connect/ConnectError.
	Data any
	// contains filtered or unexported fields
}

Packet is a decoded Socket.IO protocol packet.

func DecodePacket

func DecodePacket(s string) (Packet, error)

DecodePacket parses a Socket.IO packet from its wire form.

func (Packet) Args

func (p Packet) Args() []any

Args returns the event arguments (everything after the event name) for an Event packet, or the full array for an Ack packet.

func (Packet) Attachments

func (p Packet) Attachments() int

Attachments returns the declared number of binary attachments for a BINARY_EVENT/BINARY_ACK packet.

func (Packet) Encode

func (p Packet) Encode() (string, error)

Encode renders a packet to its Socket.IO wire form (the string carried inside an Engine.IO MESSAGE packet).

func (Packet) EncodeBinary

func (p Packet) EncodeBinary() (text string, buffers [][]byte, err error)

EncodeBinary encodes a packet, extracting any binary attachments. When the payload contains []byte values the packet type is promoted to its binary variant and the buffers are returned separately; otherwise buffers is nil. It is exported for client implementations.

func (Packet) EventName

func (p Packet) EventName() string

EventName returns the event name for an Event/BinaryEvent packet.

func (Packet) HasBinaryData added in v0.3.0

func (p Packet) HasBinaryData() bool

HasBinaryData reports whether the packet's payload contains any []byte values, i.e. whether encoding it will produce binary attachment frames.

type PacketType

type PacketType byte

PacketType identifies a Socket.IO packet.

const (
	// Connect initiates a namespace connection.
	Connect PacketType = iota
	// Disconnect leaves a namespace.
	Disconnect
	// Event carries an application event and its arguments.
	Event
	// Ack answers an Event that requested acknowledgement.
	Ack
	// ConnectError reports a failed namespace connection.
	ConnectError
	// BinaryEvent is an Event with binary attachments (decoded as text here).
	BinaryEvent
	// BinaryAck is an Ack with binary attachments (decoded as text here).
	BinaryAck
)

func (PacketType) String

func (t PacketType) String() string

String returns the Socket.IO name of the packet type (e.g. "EVENT", "ACK"), or "UNKNOWN" for an unrecognized value.

type RoomTargeter

type RoomTargeter interface {
	To(room string) *BroadcastOperator
}

RoomTargeter is anything that can scope a broadcast to a room, returning a *BroadcastOperator for further chaining (.To/.Except/.Emit). The server, a namespace, an individual socket, and an existing operator all expose this, mirroring socket.io's io.to(room) / socket.to(room) API.

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server is a Socket.IO server. It implements http.Handler and should be mounted at its configured Path.

Example

ExampleServer wires up a complete Socket.IO server the way a chat backend would. It creates a server with New, registers a connection handler that runs for every client that connects to the default namespace, and inside that handler joins the socket to a room, registers a "chat" event handler that re-broadcasts each message to everyone in that room via To(...).Emit, and installs a disconnect callback. The server is an http.Handler, so it is mounted here on an httptest.Server (a real net/http server would use http.Handle plus http.ListenAndServe at the same DefaultPath). The example then shuts everything down cleanly with Close on both the HTTP server and the Socket.IO server. The reader should take away the end-to-end shape of a server — New, OnConnection, Join, On, room broadcast, and mounting on HTTP — which mirrors the JavaScript io.on("connection")/socket.join/io.to().emit API.

package main

import (
	"fmt"
	"net/http/httptest"

	socketio "github.com/malcolmston/socketio"
)

func main() {
	io := socketio.New()
	io.OnConnection(func(s *socketio.Socket) {
		// Every socket joins a shared room on connect.
		s.Join("room1")

		// Re-broadcast each chat message to the whole room.
		s.On("chat", func(args []any) []any {
			io.To("room1").Emit("chat", args...)
			return nil
		})

		s.OnDisconnect(func(reason string) {
			// Clean up per-socket state here.
			_ = reason
		})
	})

	// A *Server is an http.Handler. In production:
	//
	//	http.Handle(socketio.DefaultPath, io)
	//	http.ListenAndServe(":3000", nil)
	//
	// Here httptest hosts it so the example is self-contained.
	ts := httptest.NewServer(io)
	defer ts.Close()
	defer io.Close()

	fmt.Println("serving Socket.IO at", socketio.DefaultPath)
}
Output:
serving Socket.IO at /socket.io/

func New

func New(opts ...Options) *Server

New creates a Server. An optional Options may be supplied.

func (*Server) Attach

func (s *Server) Attach(mux *http.ServeMux)

Attach registers the server on an http.ServeMux at its Path.

func (*Server) Close

func (s *Server) Close()

Close disconnects every connected session and shuts the server down. It does not stop the underlying http.Server (the caller owns that).

func (*Server) DisconnectSockets

func (s *Server) DisconnectSockets(closeTransport bool)

DisconnectSockets disconnects every socket in the default namespace.

func (*Server) Emit

func (s *Server) Emit(event string, args ...any)

Emit broadcasts an event to every socket in the default namespace.

func (*Server) Except added in v0.3.0

func (s *Server) Except(room string) *BroadcastOperator

Except returns a broadcast operator over the default namespace excluding every socket in the given room — the equivalent of io.except(room).

func (*Server) FetchSockets

func (s *Server) FetchSockets() []*Socket

FetchSockets returns all sockets in the default namespace.

func (*Server) Handler

func (s *Server) Handler(next http.Handler) http.Handler

Handler wraps the server so it intercepts Socket.IO requests (those under its Path) and delegates everything else to next — the Go equivalent of attaching Socket.IO to an existing HTTP server that is otherwise served by Express:

app := express.New()          // your routes
io := socketio.New()          // your socket handlers
http.ListenAndServe(":3000", io.Handler(app))

next may be any http.Handler (an *express.Application, an http.ServeMux, ...); pass nil to 404 non-Socket.IO requests.

func (*Server) In added in v0.3.0

func (s *Server) In(room string) *BroadcastOperator

In scopes a server-wide broadcast to a room in the default namespace — the equivalent of io.in(room).

func (*Server) Local added in v0.3.0

func (s *Server) Local() *BroadcastOperator

Local returns a broadcast operator over the default namespace restricted to this node — the equivalent of io.local.

func (*Server) Of

func (s *Server) Of(name string) *Namespace

Of returns the namespace with the given name, creating it if necessary. A name without a leading slash has one added.

func (*Server) OnConnection

func (s *Server) OnConnection(fn func(*Socket))

OnConnection registers a handler invoked when a socket connects to the default namespace. It is the equivalent of io.on("connection", ...).

func (*Server) OnServerEvent

func (s *Server) OnServerEvent(event string, handler func(args []any)) *Server

OnServerEvent registers a handler for server-side events delivered via ServerSideEmit. On this single-node implementation these are local; a multi-node deployment would relay them between servers through an adapter.

func (*Server) ServeHTTP

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements http.Handler and dispatches Engine.IO transport requests.

func (*Server) ServerSideEmit

func (s *Server) ServerSideEmit(event string, args ...any)

ServerSideEmit emits an event to other servers (and this one), the equivalent of io.serverSideEmit. Single-node: it invokes locally registered handlers.

func (*Server) SetBroadcaster

func (s *Server) SetBroadcaster(b Broadcaster) *Server

SetBroadcaster installs a cluster broadcaster. Once set, every room/namespace broadcast is published through it and delivered to local sockets when the message is received back, so all nodes in the cluster stay in sync.

func (*Server) Sockets

func (s *Server) Sockets() []*Socket

Sockets returns all connected sockets in the default namespace.

func (*Server) SocketsJoin

func (s *Server) SocketsJoin(rooms ...string)

SocketsJoin makes every socket in the default namespace join the given rooms.

func (*Server) SocketsLeave

func (s *Server) SocketsLeave(rooms ...string)

SocketsLeave makes every socket in the default namespace leave the given rooms.

func (*Server) To

func (s *Server) To(room string) *BroadcastOperator

To returns a broadcast operator scoped to a room in the default namespace.

func (*Server) Use

func (s *Server) Use(fn func(socket *Socket, next func(err error))) *Server

Use registers connection middleware on the default namespace.

type Socket

type Socket struct {
	// contains filtered or unexported fields
}

Socket is a single client connection to a namespace. It is the primary object applications interact with — registering event handlers, emitting events, and joining rooms.

func (*Socket) Auth

func (s *Socket) Auth() any

Auth returns the authentication payload the client sent with CONNECT.

func (*Socket) Broadcast

func (s *Socket) Broadcast() *BroadcastOperator

Broadcast returns an operator targeting every other socket in the namespace.

func (*Socket) Data

func (s *Socket) Data() map[string]any

Data returns a copy of all values stored on the socket.

func (*Socket) Delete

func (s *Socket) Delete(key string) *Socket

Delete removes a stored value.

func (*Socket) Disconnect

func (s *Socket) Disconnect(closeTransport bool)

Disconnect closes the socket, optionally closing the underlying transport.

func (*Socket) Emit

func (s *Socket) Emit(event string, args ...any) error

Emit sends an event to this socket.

func (*Socket) EmitAck

func (s *Socket) EmitAck(event string, timeout time.Duration, args ...any) ([]any, error)

EmitAck sends an event and blocks until the client acknowledges it or timeout elapses, returning the acknowledgement arguments.

func (*Socket) EmitWithAck

func (s *Socket) EmitWithAck(event string, ackFn func(args []any), args ...any) error

EmitWithAck sends an event and invokes ackFn with the client's acknowledgement arguments.

func (*Socket) Except added in v0.3.0

func (s *Socket) Except(room string) *BroadcastOperator

Except returns a broadcast operator over the namespace that excludes this socket and every member of the given room — the equivalent of socket.except(room).

func (*Socket) Get

func (s *Socket) Get(key string) (any, bool)

Get returns a value previously stored with Set, and whether it was present.

func (*Socket) GetString

func (s *Socket) GetString(key string) string

GetString returns a stored string value, or "" if missing / not a string.

func (*Socket) ID

func (s *Socket) ID() string

ID returns the socket's unique identifier.

func (*Socket) In added in v0.3.0

func (s *Socket) In(room string) *BroadcastOperator

In returns a broadcast operator that targets a room while excluding this socket — the equivalent of socket.in(room). It behaves like To.

func (*Socket) Join

func (s *Socket) Join(rooms ...string) *Socket

Join adds the socket to a room.

func (*Socket) Leave

func (s *Socket) Leave(rooms ...string) *Socket

Leave removes the socket from a room.

func (*Socket) ListenersAny added in v0.3.0

func (s *Socket) ListenersAny() []func(event string, args []any)

ListenersAny returns a snapshot of the currently registered catch-all listeners — the equivalent of socket.listenersAny().

func (*Socket) Namespace

func (s *Socket) Namespace() *Namespace

Namespace returns the namespace this socket belongs to.

func (*Socket) Off

func (s *Socket) Off(event string) *Socket

Off removes all handlers for an event.

func (*Socket) OffAny added in v0.3.0

func (s *Socket) OffAny() *Socket

OffAny removes catch-all listeners. Called with no argument it removes every catch-all listener; called with one listener it is a no-op placeholder for API symmetry (Go funcs are not comparable, so individual removal is not supported) and clears all listeners. Use ListenersAny to inspect the current set. Returns the socket for chaining.

func (*Socket) On

func (s *Socket) On(event string, handler EventHandler) *Socket

On registers a handler for an event.

func (*Socket) OnAny added in v0.3.0

func (s *Socket) OnAny(fn func(event string, args []any)) *Socket

OnAny registers a catch-all listener invoked for every inbound event on this socket, in registration order and before the named handlers run. The callback receives the event name and its arguments. Returns the socket for chaining — the equivalent of socket.onAny(listener).

func (*Socket) OnDisconnect

func (s *Socket) OnDisconnect(fn func(reason string)) *Socket

OnDisconnect registers a callback invoked when the socket disconnects.

func (*Socket) PrependAny added in v0.3.0

func (s *Socket) PrependAny(fn func(event string, args []any)) *Socket

PrependAny registers a catch-all listener at the front of the catch-all chain, so it runs before previously registered catch-all listeners — the equivalent of socket.prependAny(listener).

func (*Socket) Rooms

func (s *Socket) Rooms() []string

Rooms returns the set of rooms the socket is currently in.

func (*Socket) Set

func (s *Socket) Set(key string, value any) *Socket

Set stores an arbitrary value on the socket, persisting for the lifetime of the connection — the equivalent of Socket.IO's socket.data. Use it to attach session-like state (the authenticated user, a tenant id, ...).

func (*Socket) To

func (s *Socket) To(room string) *BroadcastOperator

To returns a broadcast operator that targets a room, excluding this socket — the equivalent of socket.to(room).

Directories

Path Synopsis
Package client is a Go Socket.IO client.
Package client is a Go Socket.IO client.
docs
gen command
Command gendocs generates a static HTML documentation site for a Go module using only the standard library (go/doc, go/parser).
Command gendocs generates a static HTML documentation site for a Go module using only the standard library (go/doc, go/parser).
Package engineio implements the Engine.IO v4 protocol codec — the transport framing layer that Socket.IO is built on.
Package engineio implements the Engine.IO v4 protocol codec — the transport framing layer that Socket.IO is built on.
examples
chat command
Command chat is a small Socket.IO server in Go: it echoes messages and broadcasts chat messages to everyone in a room.
Command chat is a small Socket.IO server in Go: it echoes messages and broadcasts chat messages to everyone in a room.
client command
Command client connects to a Socket.IO server using the Go client and exchanges a couple of events.
Command client connects to a Socket.IO server using the Go client and exchanges a couple of events.
internal
ws
Package ws is a minimal, dependency-free RFC 6455 WebSocket implementation, sufficient to carry Engine.IO/Socket.IO traffic.
Package ws is a minimal, dependency-free RFC 6455 WebSocket implementation, sufficient to carry Engine.IO/Socket.IO traffic.
Package redis provides a Redis-backed Broadcaster for socketio, enabling multi-node scale-out: broadcasts are relayed between server instances over Redis pub/sub so a message emitted on one node reaches sockets connected to any node.
Package redis provides a Redis-backed Broadcaster for socketio, enabling multi-node scale-out: broadcasts are relayed between server instances over Redis pub/sub so a message emitted on one node reaches sockets connected to any node.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL