socketio

package module
v1.0.0-...-6f70f1f Latest Latest
Warning

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

Go to latest
Published: Sep 5, 2018 License: BSD-3-Clause Imports: 16 Imported by: 0

README

socketio

socket.io/engine.io implementation in Go

GoDoc Go Report Card license Build Status

Install

go get -v -u gopkg.in/socketio.v1

Features

  • compatible with official nodejs implementation (w/o room);
  • socket.io server;
  • socket.io client (websocket only);
  • engine.io server;
  • engine.io client (websocket only);
  • binary data;
  • namespace support;
  • socket.io-msgpack-parser support;

Example

Server:

package main

import (
	"log"
	"net/http"
	"time"

	"gopkg.in/socketio.v1"
)

func main() {
	server, _ := socketio.NewServer(time.Second*25, time.Second*5, socketio.DefaultParser)
	server.Namespace("/").
		OnConnect(func(so socketio.Socket) {
			log.Println("connected:", so.RemoteAddr(), so.Sid(), so.Namespace())
		}).
		OnDisconnect(func(so socketio.Socket) {
			log.Printf("%v %v %q disconnected", so.Sid(), so.RemoteAddr(), so.Namespace())
		}).
		OnError(func(so socketio.Socket, err ...interface{}) {
			log.Println("socket", so.Sid(), so.RemoteAddr(), so.Namespace(), "error:", err)
		}).
		OnEvent("message", func(so socketio.Socket, data string) {
			log.Println(data)
		})

	http.ListenAndServe(":8081", server)
}

Client:

const io = require('socket.io-client');
const socket = io('http://localhost:8081');
var id;

socket.on('connect', function() {
  console.log('connected');
  if (id === undefined) {
    id = setInterval(function() {
      socket.emit('message', 'hello there!')
    }, 2000);
  }
});
socket.on('event', console.log);
socket.on('disconnect', function() {
  console.log('disconnected');
  if (id) {
    clearInterval(id);
    id = undefined;
  }
});
With Acknowledgements
  • Server -> Client

Server:

	so.Emit("ack", "foo", func(msg string) {
		log.Println(msg)
	})

Client:

  socket.on('ack', function(name, fn) {
    console.log(name);
    fn('bar');
  })
  • Client -> Server

Server:

	server.Namespace("/").OnEvent("foobar", func(data string) (string, string) {
		log.Println("foobar:", data)
		return "foo", "bar"
	})

Client:

  socket.emit('foobar', '-wow-', function (foo, bar) {
    console.log('foobar:', foo, bar);
  });
With Binary Data

Server:

	server.Namespace("/").
		OnEvent("binary", func(data interface{}, b *socketio.Bytes) {
			log.Println(data)
			bb, _ := b.MarshalBinary()
			log.Printf("%x", bb)
		}).
		OnConnect(func(so socketio.Socket) {
			go func() {
				for {
					select {
					case <-time.After(time.Second * 2):
						if err := so.Emit("event", "check it out!", time.Now()); err != nil {
							log.Println(err)
							return
						}
					}
				}
			}()
		})

Client:

  var ab = new ArrayBuffer(4);
  var a = new Uint8Array(ab);
  a.set([1,2,3,4]);

  id = setInterval(function() {
    socket.emit('binary', 'buf:', ab);
  }, 2000);

  socket.on('event', console.log);
Binary Helper for protobuf
import (
	"github.com/golang/protobuf/proto"
)

type ProtoMessage struct {
	proto.Message
}

func (p ProtoMessage) MarshalBinary() ([]byte, error) {
	return proto.Marshal(p.Message)
}

func (p *ProtoMessage) UnmarshalBinary(b []byte) error {
	return proto.Unmarshal(b, p.Message)
}
Binary Helper for MessagePack
import (
	"github.com/tinylib/msgp/msgp"
)

type MessagePack struct {
	Message interface {
		msgp.MarshalSizer
		msgp.Unmarshaler
	}
}

func (m MessagePack) MarshalBinary() ([]byte, error) {
	return m.Message.MarshalMsg(nil)
}

func (m *MessagePack) UnmarshalBinary(b []byte) error {
	_, err := m.Message.UnmarshalMsg(b)
	return err
}
Customized Namespace

Server:

	server.Namespace("/ditto").OnEvent("disguise", func(msg interface{}, b socketio.Bytes) {
		bb, _ := b.MarshalBinary()
		log.Printf("%v: %x", msg, bb)
	})

Client:

let ditto = io('http://localhost:8081/ditto');
ditto.emit('disguise', 'pidgey', new ArrayBuffer(8));

Parser

The encoder and decoder provided by socketio.DefaultParser is compatible with socket.io-parser, complying with revision 4 of socket.io-protocol.

An Event or Ack Packet with any data satisfying socketio.Binary interface (e.g. socketio.Bytes) would be encoded as BinaryEvent or BinaryAck Packet respectively.

socketio.MsgpackParser, compatible with socket.io-msgpack-parser, is an alternative custom parser.

nginx as Reverse Proxy (or TLS Terminator)

upstream socketio {
    ip_hash;
    server localhost:8080;
}

server {

    # ...

    location /socket.io/ {
        if ($request_method = OPTIONS) {
                add_header Content-Length 0;
                add_header Content-Type text/plain;
                add_header Access-Control-Allow-Origin "$http_origin" always;
                add_header Access-Control-Allow-Credentials 'true' always;
                add_header Access-Control-Allow-Methods "POST,GET,OPTIONS";
                add_header Access-Control-Allow-Headers "content-type";
                return 204;
        }
        proxy_pass http://socketio;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        add_header Access-Control-Allow-Origin "$http_origin" always;
        add_header Access-Control-Allow-Credentials 'true' always;
    }
}

Documentation

Index

Examples

Constants

View Source
const (
	// Revision is protocol version
	Revision = "4"
)

Variables

View Source
var (
	// ErrorNamespaceUnavaialble indicates error of client accessing to a non-existent namespace
	ErrorNamespaceUnavaialble = errors.New("namespace unavailable")
)
View Source
var (
	WebsocketTransport = engine.WebsocketTransport
)

Functions

This section is empty.

Types

type ArgsUnmarshaler

type ArgsUnmarshaler interface {
	UnmarshalArgs(args []reflect.Type, data []byte, bin [][]byte) ([]reflect.Value, error)
}

ArgsUnmarshaler unmarshals func arguments `args` from data and binary (bin, if exists). Decoder should implement ArgsUnmarshaler. For `DefaultParser`, data denotes the data in the 1st Packet (w/ type string), while bin denotes binary data in following packets if available; For `MsgpackParser`, bin is not used since all data are packed in a single Packet; args are acquired from reflection, usually by calling `newCallback(func)`

type Binary

Binary refers to binary data to be exchanged between socket.io server and client

type Bytes

type Bytes struct {
	Data []byte
}

Bytes is default implementation of Binary interface, a helper to transfer `[]byte`

func (Bytes) MarshalBinary

func (b Bytes) MarshalBinary() ([]byte, error)

MarshalBinary implements encoding.BinaryMarshaler

func (*Bytes) MarshalBinaryTo

func (b *Bytes) MarshalBinaryTo(p []byte) error

MarshalBinaryTo copies data into 'p', implementing msgp.Extension.MarshalBinaryTo

func (*Bytes) UnmarshalBinary

func (b *Bytes) UnmarshalBinary(p []byte) error

UnmarshalBinary implements encoding.BinaryUnmarshaler

type Client

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

Client is socket.io client

func NewClient

func NewClient() (c *Client)

NewClient creates a Client instance; use Dial to initialize underlying network

func (*Client) Close

func (c *Client) Close() error

Close closes underlying engine.io transport

func (*Client) Dial

func (c *Client) Dial(rawurl string, requestHeader http.Header, dialer engine.Dialer, parser Parser) (err error)

Dial connects to a socket.io server represented by `rawurl` and create Client instance on success.

func (*Client) Emit

func (c *Client) Emit(nsp string, event string, args ...interface{}) (err error)

Emit send event messages to namespace `nsp`

func (Client) EmitError

func (s Client) EmitError(arg interface{}) (err error)

EmitError implements Socket.EmitError

func (Client) LocalAddr

func (s Client) LocalAddr() net.Addr

func (*Client) Namespace

func (c *Client) Namespace(nsp string) Namespace

Namespace ensures a Namespace instance exists in client

func (*Client) OnError

func (c *Client) OnError(fn func(interface{}))

OnError registers fn as error callback

func (Client) RemoteAddr

func (s Client) RemoteAddr() net.Addr

func (*Client) Sid

func (c *Client) Sid() string

Sid returns session id assigned by socket.io server

type Decoder

type Decoder interface {
	Add(msgType MessageType, data []byte) error
	Decoded() <-chan *Packet
	ParseData(p *Packet) (string, []byte, [][]byte, error)
	ArgsUnmarshaler
}

Decoder decodes data into a Packet

type Encoder

type Encoder interface {
	Encode(p *Packet) ([]byte, [][]byte, error)
}

Encoder encodes a Packet into byte format

type MessageType

type MessageType = engine.MessageType

MessageType is alias of engine.MessageType

const (
	// MessageTypeString is alias of engine.MessageTypeString
	MessageTypeString MessageType = engine.MessageTypeString
	// MessageTypeBinary is alias of engine.MessageTypeBinary
	MessageTypeBinary MessageType = engine.MessageTypeBinary
)

type Namespace

type Namespace interface {
	// OnEvent registers event callback:
	// callback should be a valid function, 1st argument of which could be `socketio.Socket` or omitted;
	// the event callback would be called when a message received from a client with corresponding event;
	// upon invocation the corresponding `socketio.Socket` would be supplied if appropriate.
	OnEvent(event string, callback interface{}) Namespace // chainable
	// OnConnect registers fn as callback, which would be called when this Namespace is connected by a
	// client, i.e. upon receiving CONNECT packet (for non-root namespace) or connection establishment
	// ("/" namespace)
	OnConnect(fn func(so Socket)) Namespace // chainable
	// OnDisconnect registers fn as callback, which would be called when this Namespace is disconnected by a
	// client, i.e. upon receiving DISCONNECT packet or connection lost
	OnDisconnect(fn func(so Socket)) Namespace // chainable
	// OnError registers fn as callback, which would be called when error occurs in this Namespace
	OnError(fn func(so Socket, err ...interface{})) Namespace // chainable
}

Namespace is socket.io `namespace` abstraction

type Packet

type Packet struct {
	Type      PacketType  `msg:"type" json:"type"`
	Namespace string      `msg:"nsp" json:"nsp"`
	Data      interface{} `msg:"data" json:"data,omitempty"`
	ID        *uint64     `msg:"id" json:"id,omitempty"`
	// contains filtered or unexported fields
}

Packet is message abstraction, representing for data exchanged between socket.io server and client

func (*Packet) DecodeMsg

func (z *Packet) DecodeMsg(dc *msgp.Reader) (err error)

DecodeMsg implements msgp.Decodable

func (*Packet) EncodeMsg

func (z *Packet) EncodeMsg(en *msgp.Writer) (err error)

EncodeMsg implements msgp.Encodable

func (*Packet) MarshalMsg

func (z *Packet) MarshalMsg(b []byte) (o []byte, err error)

MarshalMsg implements msgp.Marshaler

func (*Packet) Msgsize

func (z *Packet) Msgsize() (s int)

Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message

func (*Packet) UnmarshalMsg

func (z *Packet) UnmarshalMsg(bts []byte) (o []byte, err error)

UnmarshalMsg implements msgp.Unmarshaler

type PacketType

type PacketType byte

PacketType indicates type of a Packet

const (
	PacketTypeConnect PacketType = iota
	PacketTypeDisconnect
	PacketTypeEvent
	PacketTypeAck
	PacketTypeError
	PacketTypeBinaryEvent
	PacketTypeBinaryAck
)

func (PacketType) String

func (p PacketType) String() string

type Parser

type Parser interface {
	Encoder() Encoder
	Decoder() Decoder
}

Parser provides Encoder and Decoder instance, like a factory

var (
	// ErrUnknownPacket indicates packet invalid or unknown when parser encoding/decoding data
	ErrUnknownPacket = errors.New("unknown packet")
	// DefaultParser is default parser implementation for socket.io, compatible with `socket.io-parser`.
	DefaultParser Parser = &defaultParser{}
	// MsgpackParser is msgpack parser implementation for socket.io, compatible with `socket.io-msgpack-parser`.
	MsgpackParser Parser = &msgpackParser{}
)

type Server

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

Server is socket.io server implementation

Example
server, _ := NewServer(time.Second*5, time.Second*5, DefaultParser)
var onConnect = func(so Socket) {
	log.Println("connected:", so.RemoteAddr(), so.Sid(), so.Namespace())
	go func() {
		for {
			<-time.After(time.Second * 2)
			if err := so.Emit("event", "check it out!", time.Now()); err != nil {
				log.Println("emit:", err)
				return
			}
		}
	}()
	so.Emit("event", "hello world!")
}

var onDisconnect = func(so Socket) {
	log.Printf("%v %v %q disconnected", so.Sid(), so.RemoteAddr(), so.Namespace())
}

var onError = func(so Socket, err ...interface{}) {
	log.Println("socket", so.Sid(), so.RemoteAddr(), so.Namespace(), "error:", err)
}

server.Namespace("/").
	OnConnect(onConnect).
	OnDisconnect(onDisconnect).
	OnError(onError).
	OnEvent("message", func(so Socket, data string) {
		if err := so.Emit("ack", "woot", func(msg string, b *Bytes) {
			bb, _ := b.MarshalBinary()
			log.Printf("%s=> %x", msg, bb)
		}); err != nil {
			log.Println("emit:", err)
		}
	}).
	OnEvent("binary", func(data interface{}, b Bytes) {
		bb, _ := b.MarshalBinary()
		log.Printf("%s <- %x", data, bb)
	}).
	OnEvent("foobar", func(data string) (string, string) {
		log.Println("foobar:", data)
		return "foo", "bar"
	})

server.Namespace("/ditto").
	OnConnect(func(so Socket) {
		log.Println("connected:", so.RemoteAddr(), so.Sid(), so.Namespace())
	}).
	OnDisconnect(onDisconnect).
	OnError(onError).
	OnEvent("disguise", func(msg interface{}, b Bytes) {
		bb, _ := b.MarshalBinary()
		log.Printf("%v: %x", msg, bb)
	})

server.OnError(func(err error) {
	log.Printf("server error: %v", err)
})
defer server.Close()
log.Fatalln(http.ListenAndServe("localhost:8081", server))
Output:

Example (WithMsgpackParser)
server, _ := NewServer(time.Second*5, time.Second*5, MsgpackParser)
server.Namespace("/").
	OnConnect(func(so Socket) {
		log.Println("connected:", so.RemoteAddr(), so.Sid(), so.Namespace())
		so.Emit("event", "hello world!", time.Now())
	}).
	OnDisconnect(func(so Socket) {
		log.Printf("%v %v %q disconnected", so.Sid(), so.RemoteAddr(), so.Namespace())
	}).
	OnEvent("message", func(b msgp.Raw, data foobar) {
		log.Printf("%x %v", b, data)
	}).
	OnError(func(so Socket, err ...interface{}) {
		log.Println("socket", so.Sid(), so.RemoteAddr(), so.Namespace(), "error:", err)
	})
server.OnError(func(err error) {
	log.Println("server err:", err)
})
defer server.Close()
log.Fatalln(http.ListenAndServe("localhost:8081", server))
Output:

func NewServer

func NewServer(interval, timeout time.Duration, parser Parser) (server *Server, err error)

NewServer creates a socket.io server instance upon underlying engine.io transport

func (*Server) Close

func (s *Server) Close() error

Close closes underlying engine.io transport

func (*Server) Namespace

func (s *Server) Namespace(nsp string) Namespace

Namespace ensures a Namespace instance exists in server

func (*Server) OnError

func (s *Server) OnError(fn func(err error))

OnError registers fn as callback for error handling

func (*Server) ServeHTTP

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

ServeHTTP implements http.Handler interface

type Socket

type Socket interface {
	Emit(event string, args ...interface{}) (err error)
	EmitError(arg interface{}) (err error)
	Namespace() string
	RemoteAddr() net.Addr
	LocalAddr() net.Addr
	Sid() string
	io.Closer
}

Socket is abstraction of bidirectional socket.io connection

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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