Documentation
¶
Overview ¶
Package socketio implements the Socket.IO v5 protocol layer on top of the engineio transport package.
Protocol reference: https://socket.io/docs/v4/socket-io-protocol/
Index ¶
- Variables
- type BroadcastOperator
- type Client
- func (c *Client) Close() error
- func (c *Client) ConnectNamespace(ctx context.Context, nsp string, data map[string]any) error
- func (c *Client) Connected(nsp string) bool
- func (c *Client) DisconnectNamespace(nsp string)
- func (c *Client) Emit(nsp, event string, args ...any) error
- func (c *Client) EmitWithAck(nsp, event string, cb func(args []any), args ...any) (int64, error)
- func (c *Client) ID(nsp string) string
- func (c *Client) OnConnect(nsp string, f func())
- func (c *Client) OnConnectError(nsp string, f func(err error))
- func (c *Client) OnDisconnect(nsp string, f func(reason string))
- func (c *Client) OnEvent(nsp, event string, f any)
- func (c *Client) SetLogger(l engineio.Logger)
- type Middleware
- type Options
- type Packet
- type PacketType
- type Server
- func (s *Server) BroadcastToNamespace(nsp, event string, args ...any)
- func (s *Server) BroadcastToRoom(nsp, room, event string, args ...any)
- func (s *Server) Close()
- func (s *Server) Engine() *engineio.Server
- func (s *Server) Namespace(nsp string) *namespace
- func (s *Server) OnConnect(nsp string, f func(*Socket) error)
- func (s *Server) OnDisconnect(nsp string, f func(*Socket, string))
- func (s *Server) OnEvent(nsp, event string, f any)
- func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)
- func (s *Server) SetLogger(l engineio.Logger)
- func (s *Server) Use(nsp string, m Middleware)
- type Socket
- func (s *Socket) Broadcast(event string, args ...any)
- func (s *Socket) BroadcastToRoom(room, event string, args ...any)
- func (s *Socket) Connected() bool
- func (s *Socket) Disconnect()
- func (s *Socket) Emit(event string, args ...any) error
- func (s *Socket) EmitWithAck(event string, cb func(args []any), args ...any) (int64, error)
- func (s *Socket) Engine() *engineio.Socket
- func (s *Socket) ID() string
- func (s *Socket) JoinRoom(room string)
- func (s *Socket) LeaveRoom(room string)
- func (s *Socket) Nsp() string
- func (s *Socket) RemoteAddr() string
- func (s *Socket) To(room string) *BroadcastOperator
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrNotConnected is returned when emitting on a disconnected socket. ErrNotConnected = errors.New("socketio: socket is not connected") )
sentinel errors exposed to users.
var NopLogger engineio.Logger = slogLogger{/* contains filtered or unexported fields */}
NopLogger discards all log output.
Functions ¶
This section is empty.
Types ¶
type BroadcastOperator ¶
type BroadcastOperator struct {
// contains filtered or unexported fields
}
BroadcastOperator targets a subset of a namespace for broadcasting.
func (*BroadcastOperator) Emit ¶
func (b *BroadcastOperator) Emit(event string, args ...any)
Emit sends the event to the targeted sockets.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a Socket.IO client. It maintains one Engine.IO connection multiplexing any number of namespaces, and mirrors the official socket.io-client behaviour for events, acknowledgements, binary payloads and reconnection.
func Dial ¶
Dial connects to a Socket.IO server. It blocks until the root namespace CONNECT completes or ctx is cancelled.
Example ¶
ExampleDial demonstrates a full Socket.IO round trip: start a server, connect a client, emit an event and receive the acknowledgement. The handler's return value becomes the ack payload.
package main
import (
"context"
"fmt"
"log"
"net/http/httptest"
"time"
"github.com/kingecg/gosocketio/socketio"
)
func main() {
srv := socketio.NewServer(nil)
srv.OnEvent("/", "ping", func(s *socketio.Socket) string {
return "pong"
})
httpSrv := httptest.NewServer(srv)
defer httpSrv.Close()
c, err := socketio.Dial(context.Background(), httpSrv.URL+"/socket.io/", nil)
if err != nil {
log.Fatal(err)
}
defer c.Close()
ack := make(chan []any, 1)
if _, err := c.EmitWithAck("/", "ping", func(args []any) {
ack <- args
}); err != nil {
log.Fatal(err)
}
select {
case args := <-ack:
fmt.Printf("ack: %v\n", args)
case <-time.After(5 * time.Second):
log.Fatal("timed out waiting for ack")
}
}
Output: ack: [pong]
func (*Client) Close ¶
Close terminates the session. Pending acknowledgements are dropped and every connected namespace reports a disconnect with reason "io client disconnect".
func (*Client) ConnectNamespace ¶
ConnectNamespace connects an additional namespace, blocking until the CONNECT acknowledgement arrives or ctx is cancelled.
func (*Client) DisconnectNamespace ¶
DisconnectNamespace disconnects a namespace, notifying the server.
func (*Client) EmitWithAck ¶
EmitWithAck sends an event that requires an acknowledgement. cb is invoked with the server's reply once it arrives.
func (*Client) OnConnect ¶
OnConnect registers a handler invoked when a namespace connection completes. It fires again after each successful reconnection.
func (*Client) OnConnectError ¶
OnConnectError registers a handler invoked when a namespace connection is rejected, carrying the error reported by the server.
func (*Client) OnDisconnect ¶
OnDisconnect registers a handler invoked when a namespace disconnects, carrying the Socket.IO disconnect reason.
type Middleware ¶
Middleware is invoked with the CONNECT payload before a connection to the namespace is accepted. Returning a non-nil error rejects the connection with a CONNECT_ERROR packet carrying the error message.
type Options ¶
type Options struct {
// Transports overrides the Engine.IO transports.
Transports []string
// Engine passes extra options to engineio.Dial.
Engine *engineio.Options
// Auth is sent as the root namespace CONNECT payload.
Auth map[string]any
// Reconnection enables automatic reconnection after an unexpected
// close of the underlying transport.
Reconnection bool
// ReconnectionAttempts limits consecutive reconnection attempts. Zero
// (the default) means retry forever.
ReconnectionAttempts int
// ReconnectionDelay is the base delay before the first reconnection
// attempt. Default: 1s.
ReconnectionDelay time.Duration
// ReconnectionDelayMax caps the exponentially backed-off delay.
// Default: 5s.
ReconnectionDelayMax time.Duration
// RandomizationFactor in [0,1] jitters the delay so simultaneous
// clients do not reconnect in lockstep. Default: 0.5.
RandomizationFactor float64
// Timeout bounds each reconnection attempt (Engine.IO dial plus all
// namespace CONNECTs). Default: 10s.
Timeout time.Duration
}
Options configures a Socket.IO client session.
type Packet ¶
type Packet struct {
Type PacketType
Nsp string
ID int64
Data any
// Attachments is the number of binary buffers following a binary
// packet. It is only meaningful for BinaryEvent/BinaryAck packets.
Attachments int
}
Packet is a Socket.IO packet. Data holds the decoded JSON payload:
- Connect / ConnectError: map[string]any (or nil)
- Event: []any ([eventName, args...])
- Ack: []any ([args...])
A negative ID means the packet carries no acknowledgement id.
type PacketType ¶
type PacketType byte
PacketType is the Socket.IO packet type.
const ( Connect PacketType = '0' Disconnect PacketType = '1' Event PacketType = '2' Ack PacketType = '3' ConnectError PacketType = '4' BinaryEvent PacketType = '5' BinaryAck PacketType = '6' )
Socket.IO packet types.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is a Socket.IO server layered on top of an Engine.IO server. It implements http.Handler.
func NewServer ¶
NewServer creates a Socket.IO server with its own Engine.IO server. opts customizes the underlying Engine.IO layer.
func (*Server) BroadcastToNamespace ¶
BroadcastToNamespace sends an event to every socket in a namespace.
func (*Server) BroadcastToRoom ¶
BroadcastToRoom sends an event to every socket in a namespace's room.
Example ¶
ExampleServer_BroadcastToRoom demonstrates room-scoped broadcasting: every socket that joined the room receives the message (including the sender), while sockets outside the room do not.
package main
import (
"context"
"fmt"
"log"
"net/http/httptest"
"time"
"github.com/kingecg/gosocketio/socketio"
)
func main() {
srv := socketio.NewServer(nil)
srv.OnEvent("/", "join", func(s *socketio.Socket, room string) string {
s.JoinRoom(room)
return "joined"
})
srv.OnEvent("/", "room message", func(s *socketio.Socket, text string) {
srv.BroadcastToRoom("/", "lobby", "room message", text)
})
httpSrv := httptest.NewServer(srv)
defer httpSrv.Close()
connect := func() *socketio.Client {
c, err := socketio.Dial(context.Background(), httpSrv.URL+"/socket.io/", nil)
if err != nil {
log.Fatal(err)
}
return c
}
alice := connect()
defer alice.Close()
bob := connect()
defer bob.Close()
// The server acknowledges each join, so membership is guaranteed before
// the broadcast happens.
join := func(c *socketio.Client) {
done := make(chan []any, 1)
if _, err := c.EmitWithAck("/", "join", func(args []any) { done <- args }, "lobby"); err != nil {
log.Fatal(err)
}
select {
case <-done:
case <-time.After(5 * time.Second):
log.Fatal("timed out waiting for join ack")
}
}
join(alice)
join(bob)
// Carol connects but never joins the room.
carol := connect()
defer carol.Close()
carolGot := make(chan struct{})
carol.OnEvent("/", "room message", func(text string) {
close(carolGot)
})
got := make(chan string, 2)
alice.OnEvent("/", "room message", func(text string) { got <- "alice: " + text })
bob.OnEvent("/", "room message", func(text string) { got <- "bob: " + text })
if err := alice.Emit("/", "room message", "hello lobby"); err != nil {
log.Fatal(err)
}
for i := 0; i < 2; i++ {
select {
case m := <-got:
fmt.Println(m)
case <-time.After(5 * time.Second):
log.Fatal("timed out waiting for room broadcast")
}
}
select {
case <-carolGot:
log.Fatal("carol received a message despite not being in the room")
default:
fmt.Println("carol: no message (not in the room)")
}
}
Output: alice: hello lobby bob: hello lobby carol: no message (not in the room)
func (*Server) OnConnect ¶
OnConnect registers a handler invoked when a client connects to a namespace. Returning an error rejects the connection with a CONNECT_ERROR packet.
func (*Server) OnDisconnect ¶
OnDisconnect registers a handler invoked when a socket leaves a namespace.
func (*Server) OnEvent ¶
OnEvent registers a handler for an event within a namespace.
Example ¶
ExampleServer_OnEvent demonstrates a server-side event handler receiving typed arguments. []byte arguments travel as binary attachments and are reconstructed before the handler is invoked.
package main
import (
"context"
"fmt"
"log"
"net/http/httptest"
"time"
"github.com/kingecg/gosocketio/socketio"
)
func main() {
srv := socketio.NewServer(nil)
srv.OnEvent("/", "download", func(s *socketio.Socket, name string, data []byte) {
fmt.Printf("server received %q (%d bytes)\n", name, len(data))
s.Emit("file", name, data)
})
httpSrv := httptest.NewServer(srv)
defer httpSrv.Close()
c, err := socketio.Dial(context.Background(), httpSrv.URL+"/socket.io/", nil)
if err != nil {
log.Fatal(err)
}
defer c.Close()
got := make(chan struct{})
c.OnEvent("/", "file", func(name string, data []byte) {
fmt.Printf("client received %q (%d bytes)\n", name, len(data))
close(got)
})
if err := c.Emit("/", "download", "logo.png", []byte{0x89, 0x50, 0x4e, 0x47}); err != nil {
log.Fatal(err)
}
select {
case <-got:
case <-time.After(5 * time.Second):
log.Fatal("timed out waiting for binary echo")
}
}
Output: server received "logo.png" (4 bytes) client received "logo.png" (4 bytes)
func (*Server) ServeHTTP ¶
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP implements http.Handler, delegating to the Engine.IO server.
func (*Server) Use ¶
func (s *Server) Use(nsp string, m Middleware)
Use registers a connection middleware for a namespace.
Example ¶
ExampleServer_Use demonstrates a connection middleware that validates the CONNECT payload (for example an auth token) and rejects unauthorized clients with a CONNECT_ERROR.
package main
import (
"context"
"errors"
"fmt"
"log"
"net/http/httptest"
"github.com/kingecg/gosocketio/socketio"
)
func main() {
srv := socketio.NewServer(nil)
srv.Use("/", func(s *socketio.Socket, data map[string]any) error {
if token, _ := data["token"].(string); token != "secret" {
return errors.New("unauthorized")
}
return nil
})
httpSrv := httptest.NewServer(srv)
defer httpSrv.Close()
good, err := socketio.Dial(context.Background(), httpSrv.URL+"/socket.io/", &socketio.Options{
Auth: map[string]any{"token": "secret"},
})
if err != nil {
log.Fatal(err)
}
fmt.Println("authorized: connected")
good.Close()
if _, err := socketio.Dial(context.Background(), httpSrv.URL+"/socket.io/", &socketio.Options{
Auth: map[string]any{"token": "wrong"},
}); err != nil {
fmt.Println("unauthorized:", err)
}
}
Output: authorized: connected unauthorized: unauthorized
type Socket ¶
type Socket struct {
// contains filtered or unexported fields
}
Socket is a single Socket.IO connection to a namespace. It is created when a client sends a CONNECT packet for the namespace and stays valid until the client or server disconnects that namespace.
func (*Socket) Broadcast ¶
Broadcast sends an event to every socket in the namespace except this one.
func (*Socket) BroadcastToRoom ¶
BroadcastToRoom sends an event to every socket in room except this one.
func (*Socket) Disconnect ¶
func (s *Socket) Disconnect()
Disconnect disconnects this namespace socket, notifying the client.
func (*Socket) EmitWithAck ¶
EmitWithAck sends an event that requires an acknowledgement. cb is invoked with the client's reply once it arrives.
func (*Socket) RemoteAddr ¶
RemoteAddr returns the remote address of the underlying connection.
func (*Socket) To ¶
func (s *Socket) To(room string) *BroadcastOperator
To returns a broadcast operator targeting a single room.