gws

package module
v1.5.2 Latest Latest
Warning

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

Go to latest
Published: May 27, 2023 License: MIT Imports: 20 Imported by: 64

README

gws

event-driven go websocket server & client

Build Status MIT licensed Go Version codecov Go Report Card

Highlight
  • IO multiplexing support, concurrent message processing and asynchronous non-blocking message writing
  • High IOPS and low latency, low CPU usage
  • Support fast parsing WebSocket protocol directly from TCP, faster handshake, lower memory usage
  • Fully passes the WebSocket autobahn-testsuite
Attention
  • The errors returned by the gws.Conn export methods are ignored, and are handled internally
  • When an exception occurs, one and only one of OnError/OnClose will be triggered
  • Transferring large files with gws tends to block the connection
Install
go get -v github.com/lxzan/gws@latest
Event
type Event interface {
	OnOpen(socket *Conn)
	OnError(socket *Conn, err error)
	OnClose(socket *Conn, code uint16, reason []byte)
	OnPing(socket *Conn, payload []byte)
	OnPong(socket *Conn, payload []byte)
	OnMessage(socket *Conn, message *Message)
}
Quick Start
package main

import "github.com/lxzan/gws"

func main() {
	gws.NewServer(new(gws.BuiltinEventHandler), nil).Run(":6666")
}
Best Practice
package main

import (
	"github.com/lxzan/gws"
	"time"
)

const PingInterval = 10 * time.Second

func main() {
	options := &gws.ServerOption{ReadAsyncEnabled: true, ReadAsyncGoLimit: 4, CompressEnabled: true}
	gws.NewServer(new(Handler), options).Run(":6666")
}

type Handler struct{}

func (c *Handler) OnOpen(socket *gws.Conn) { _ = socket.SetDeadline(time.Now().Add(2 * PingInterval)) }

func (c *Handler) DeleteSession(socket *gws.Conn) {}

func (c *Handler) OnError(socket *gws.Conn, err error) { c.DeleteSession(socket) }

func (c *Handler) OnClose(socket *gws.Conn, code uint16, reason []byte) { c.DeleteSession(socket) }

func (c *Handler) OnPing(socket *gws.Conn, payload []byte) {
	_ = socket.SetDeadline(time.Now().Add(2 * PingInterval))
	_ = socket.WritePong(nil)
}

func (c *Handler) OnPong(socket *gws.Conn, payload []byte) {}

func (c *Handler) OnMessage(socket *gws.Conn, message *gws.Message) {
	defer message.Close()
}
Usage
Upgrade from HTTP
package main

import (
	"github.com/lxzan/gws"
	"log"
	"net/http"
)

func main() {
	upgrader := gws.NewUpgrader(new(gws.BuiltinEventHandler), &gws.ServerOption{
		Authorize: func(r *http.Request, session gws.SessionStorage) bool {
			session.Store("username", r.URL.Query().Get("username"))
			return true
		},
	})

	http.HandleFunc("/connect", func(writer http.ResponseWriter, request *http.Request) {
		socket, err := upgrader.Upgrade(writer, request)
		if err != nil {
			log.Printf(err.Error())
			return
		}
		socket.ReadLoop()
	})

	if err := http.ListenAndServe(":6666", nil); err != nil {
		log.Fatalf("%v", err)
	}
}
Unix Domain Socket
  • server
package main

import (
	"log"
	"net"
	"github.com/lxzan/gws"
)

func main() {
	listener, err := net.Listen("unix", "/tmp/gws.sock")
	if err != nil {
		log.Println(err.Error())
		return
	}
	var app = gws.NewServer(new(gws.BuiltinEventHandler), nil)
	if err := app.RunListener(listener); err != nil {
		log.Println(err.Error())
	}
}
  • client
package main

import (
	"log"
	"net"
	"github.com/lxzan/gws"
)

func main() {
	conn, err := net.Dial("unix", "/tmp/gws.sock")
	if err != nil {
		log.Println(err.Error())
		return
	}

	option := gws.ClientOption{}
	socket, _, err := gws.NewClientFromConn(new(gws.BuiltinEventHandler), &option, conn)
	if err != nil {
		log.Println(err.Error())
		return
	}
	socket.ReadLoop()
}
Broadcast
func Broadcast(conns []*gws.Conn, opcode gws.Opcode, payload []byte) {
	for _, item := range conns {
		_ = item.WriteAsync(opcode, payload)
	}
}
Autobahn Test
cd examples/autobahn
mkdir reports
docker run -it --rm \
    -v ${PWD}/config:/config \
    -v ${PWD}/reports:/reports \
    crossbario/autobahn-testsuite \
    wstest -m fuzzingclient -s /config/fuzzingclient.json
Benchmark
  • Fedora 38 Workstation (Linux 6.2 Kernel)
  • Connection = 1000
  • GOMAXPROCS = 2
Compress Disabled

performance

Compress Enabled

performance

Communication
QQ
Acknowledgments

The following project had particular influence on gws's design.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type BuiltinEventHandler added in v1.3.0

type BuiltinEventHandler struct{}

func (BuiltinEventHandler) OnClose added in v1.3.0

func (b BuiltinEventHandler) OnClose(socket *Conn, code uint16, reason []byte)

func (BuiltinEventHandler) OnError added in v1.3.0

func (b BuiltinEventHandler) OnError(socket *Conn, err error)

func (BuiltinEventHandler) OnMessage added in v1.3.0

func (b BuiltinEventHandler) OnMessage(socket *Conn, message *Message)

func (BuiltinEventHandler) OnOpen added in v1.3.0

func (b BuiltinEventHandler) OnOpen(socket *Conn)

func (BuiltinEventHandler) OnPing added in v1.3.0

func (b BuiltinEventHandler) OnPing(socket *Conn, payload []byte)

func (BuiltinEventHandler) OnPong added in v1.3.0

func (b BuiltinEventHandler) OnPong(socket *Conn, payload []byte)

type ClientOption added in v1.4.2

type ClientOption struct {
	// 写缓冲区的大小, v1.4.5版本此参数被废弃
	// Deprecated: Size of the write buffer, v1.4.5 version of this parameter is deprecated
	WriteBufferSize     int
	ReadAsyncEnabled    bool
	ReadAsyncGoLimit    int
	ReadAsyncCap        int
	ReadMaxPayloadSize  int
	ReadBufferSize      int
	WriteAsyncCap       int
	WriteMaxPayloadSize int
	CompressEnabled     bool
	CompressLevel       int
	CompressThreshold   int
	CheckUtf8Enabled    bool

	// 连接地址, 例如 wss://example.com/connect
	// service address, eg: wss://example.com/connect
	Addr string
	// 额外的请求头
	// extra request header
	RequestHeader http.Header
	// dial timeout
	// 连接超时时间
	DialTimeout time.Duration
	// TLS设置
	// tls config
	TlsConfig *tls.Config
}

type Comparable added in v1.5.1

type Comparable interface {
	string | int | int64 | int32 | uint | uint64 | uint32
}

ConcurrentMap used to store websocket connections in the IM server 用来存储IM等服务的连接

type ConcurrentMap added in v1.2.5

type ConcurrentMap[K Comparable, V any] struct {
	// contains filtered or unexported fields
}

ConcurrentMap used to store websocket connections in the IM server 用来存储IM等服务的连接

func NewConcurrentMap added in v1.2.5

func NewConcurrentMap[K Comparable, V any](segments uint64) *ConcurrentMap[K, V]

func (*ConcurrentMap[K, V]) Delete added in v1.2.5

func (c *ConcurrentMap[K, V]) Delete(key K)

func (*ConcurrentMap[K, V]) Len added in v1.2.5

func (c *ConcurrentMap[K, V]) Len() int

func (*ConcurrentMap[K, V]) Load added in v1.2.5

func (c *ConcurrentMap[K, V]) Load(key K) (value V, exist bool)

func (*ConcurrentMap[K, V]) Range added in v1.2.5

func (c *ConcurrentMap[K, V]) Range(f func(key K, value V) bool)

Range calls f sequentially for each key and value present in the map. If f returns false, range stops the iteration.

func (*ConcurrentMap[K, V]) Store added in v1.2.5

func (c *ConcurrentMap[K, V]) Store(key K, value V)

type Config added in v1.2.0

type Config struct {

	// 是否开启异步读, 开启的话会并行调用OnMessage
	// Whether to enable asynchronous reading, if enabled OnMessage will be called in parallel
	ReadAsyncEnabled bool

	// 异步读的最大并行协程数量
	// Maximum number of parallel concurrent processes for asynchronous reads
	ReadAsyncGoLimit int

	// 异步读的容量限制, 容量溢出将会返回错误
	// Capacity limit for asynchronous reads, overflow will return an error
	ReadAsyncCap int

	// 最大读取的消息内容长度
	// Maximum read message content length
	ReadMaxPayloadSize int

	// 读缓冲区的大小
	// Size of the read buffer
	ReadBufferSize int

	// 异步写的容量限制, 容量溢出将会返回错误
	// Capacity limit for asynchronous writes, overflow will return an error
	WriteAsyncCap int

	// 最大写入的消息内容长度
	// Maximum length of written message content
	WriteMaxPayloadSize int

	// 写缓冲区的大小, v1.4.5版本此参数被废弃
	// Deprecated: Size of the write buffer, v1.4.5 version of this parameter is deprecated
	WriteBufferSize int

	// 是否开启数据压缩
	// Whether to turn on data compression
	CompressEnabled bool

	// 压缩级别
	// Compress level
	CompressLevel int

	// 压缩阈值, 低于阈值的消息不会被压缩
	// Compression threshold, messages below the threshold will not be compressed
	CompressThreshold int

	// CompressorNum 压缩器数量
	// 数值越大竞争的概率越小, 但是会耗费大量内存, 注意取舍
	// Number of compressors
	// The higher the value the lower the probability of competition, but it will consume a lot of memory, so be careful about the trade-off
	CompressorNum int

	// 是否检查文本utf8编码, 关闭性能会好点
	// Whether to check the text utf8 encoding, turn off the performance will be better
	CheckUtf8Enabled bool
	// contains filtered or unexported fields
}

type Conn

type Conn struct {
	// store session information
	SessionStorage SessionStorage
	// contains filtered or unexported fields
}

func NewClient added in v1.4.2

func NewClient(handler Event, option *ClientOption) (client *Conn, resp *http.Response, e error)

NewClient 创建WebSocket客户端; 支持ws/wss Create WebSocket client, support ws/wss

func NewClientFromConn added in v1.5.1

func NewClientFromConn(handler Event, option *ClientOption, conn net.Conn) (client *Conn, resp *http.Response, e error)

NewClientFromConn

func (*Conn) LocalAddr added in v1.0.1

func (c *Conn) LocalAddr() net.Addr

func (*Conn) NetConn added in v1.2.10

func (c *Conn) NetConn() net.Conn

NetConn get tcp/tls/... conn

func (*Conn) ReadLoop added in v1.4.8

func (c *Conn) ReadLoop()

ReadLoop start a read message loop 启动一个读消息的死循环

func (*Conn) RemoteAddr added in v1.0.1

func (c *Conn) RemoteAddr() net.Addr

func (*Conn) SetDeadline

func (c *Conn) SetDeadline(t time.Time) error

SetDeadline sets deadline

func (*Conn) SetReadDeadline added in v1.1.2

func (c *Conn) SetReadDeadline(t time.Time) error

SetReadDeadline sets read deadline

func (*Conn) SetWriteDeadline added in v1.1.2

func (c *Conn) SetWriteDeadline(t time.Time) error

SetWriteDeadline sets write deadline

func (*Conn) WriteAsync added in v1.3.0

func (c *Conn) WriteAsync(opcode Opcode, payload []byte) error

WriteAsync 异步非阻塞地写入消息 Write messages asynchronously and non-blockingly

func (*Conn) WriteClose

func (c *Conn) WriteClose(code uint16, reason []byte)

WriteClose code: https://developer.mozilla.org/zh-CN/docs/Web/API/CloseEvent#status_codes 通过emitError发送关闭帧, 将连接状态置为关闭, 用于服务端主动断开连接 没有特殊原因的话, 建议code=0, reason=nil Send a close frame via emitError to set the connection state to closed, for server-initiated disconnection If there is no special reason, we suggest code=0, reason=nil

func (*Conn) WriteMessage added in v1.1.0

func (c *Conn) WriteMessage(opcode Opcode, payload []byte) error

WriteMessage 发送消息

func (*Conn) WritePing

func (c *Conn) WritePing(payload []byte) error

WritePing write ping frame

func (*Conn) WritePong

func (c *Conn) WritePong(payload []byte) error

WritePong write pong frame

func (*Conn) WriteString added in v1.2.10

func (c *Conn) WriteString(s string) error

WriteString write text frame force convert string to []byte

type Event added in v1.1.2

type Event interface {
	// 建立连接事件
	// WebSocket connection was successfully established
	OnOpen(socket *Conn)

	// 错误事件
	// IO错误, 协议错误, 压缩解压错误...
	// 一旦发生错误, 连接会自动关闭
	// IO errors, protocol errors, compression and decompression errors...
	// In case of an error, the connection will be closed automatically
	OnError(socket *Conn, err error)

	// 关闭事件
	// 接收到了网络连接另一端发送的关闭帧
	// Close frame is received from the other end of the network connection
	OnClose(socket *Conn, code uint16, reason []byte)

	// 心跳探测事件
	// Received a ping frame
	OnPing(socket *Conn, payload []byte)

	// 心跳响应事件
	// Received a pong frame
	OnPong(socket *Conn, payload []byte)

	// 消息事件
	// 如果开启了ReadAsyncEnabled, 会并行地调用OnMessage; 没有做recover处理.
	// If ReadAsyncEnabled is enabled, OnMessage is called in parallel. No recover is done.
	OnMessage(socket *Conn, message *Message)
}

WebSocket Event 出现异常时, OnError/OnClose中有且只有一个会被触发, 由CAS保证 When an exception occurs, one and only one of OnError/OnClose will be triggered, guaranteed by CAS

type Message

type Message struct {

	// 操作码
	Opcode Opcode

	// 消息内容
	Data *bytes.Buffer
	// contains filtered or unexported fields
}

func (*Message) Bytes

func (c *Message) Bytes() []byte

func (*Message) Close

func (c *Message) Close() error

Close recycle buffer

func (*Message) Read added in v1.1.0

func (c *Message) Read(p []byte) (n int, err error)

type Opcode

type Opcode uint8
const (
	OpcodeContinuation    Opcode = 0x0
	OpcodeText            Opcode = 0x1
	OpcodeBinary          Opcode = 0x2
	OpcodeCloseConnection Opcode = 0x8
	OpcodePing            Opcode = 0x9
	OpcodePong            Opcode = 0xA
)

func (Opcode) IsDataFrame added in v1.1.2

func (c Opcode) IsDataFrame() bool

type Server added in v1.4.7

type Server struct {

	// OnConnect 建立连接事件, 用于处理限流, 熔断和安全问题; 返回错误将会断开连接.
	// Creates connection events for current limit, fuse and security issues; returning an error will disconnect.
	OnConnect func(conn net.Conn) error

	// OnError 接收握手过程中产生的错误回调
	// Receive error callbacks generated during the handshake
	OnError func(conn net.Conn, err error)
	// contains filtered or unexported fields
}

func NewServer added in v1.4.7

func NewServer(eventHandler Event, option *ServerOption) *Server

NewServer 创建websocket服务器 create a websocket server

func (*Server) Run added in v1.4.7

func (c *Server) Run(addr string) error

Run runs ws server addr: Address of the listener

func (*Server) RunListener added in v1.4.9

func (c *Server) RunListener(listener net.Listener) error

func (*Server) RunTLS added in v1.4.7

func (c *Server) RunTLS(addr string, certFile, keyFile string) error

RunTLS runs wss server addr: Address of the listener config: tls config

type ServerOption added in v1.4.0

type ServerOption struct {

	// 写缓冲区的大小, v1.4.5版本此参数被废弃
	// Deprecated: Size of the write buffer, v1.4.5 version of this parameter is deprecated
	WriteBufferSize     int
	ReadAsyncEnabled    bool
	ReadAsyncGoLimit    int
	ReadAsyncCap        int
	ReadMaxPayloadSize  int
	ReadBufferSize      int
	WriteAsyncCap       int
	WriteMaxPayloadSize int
	CompressEnabled     bool
	CompressLevel       int
	CompressThreshold   int
	CompressorNum       int
	CheckUtf8Enabled    bool

	// WebSocket子协议, 一般不需要设置
	// WebSocket subprotocol, usually no need to set
	Subprotocols []string

	// 连接握手时添加的额外的响应头, 如果客户端不支持就不要传
	// https://www.rfc-editor.org/rfc/rfc6455.html#section-1.3
	// attention: client may not support custom response header, use nil instead
	ResponseHeader http.Header

	// 鉴权
	// Authentication of requests for connection establishment
	Authorize func(r *http.Request, session SessionStorage) bool
	// contains filtered or unexported fields
}

type SessionStorage added in v1.2.3

type SessionStorage interface {
	Load(key string) (value interface{}, exist bool)
	Delete(key string)
	Store(key string, value interface{})
	Range(f func(key string, value interface{}) bool)
}

SessionStorage because sync.Map is not easy to debug, so I implemented my own map. if you don't like it, use sync.Map instead.

type Upgrader

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

func NewUpgrader added in v1.2.11

func NewUpgrader(eventHandler Event, option *ServerOption) *Upgrader

func (*Upgrader) Upgrade

func (c *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request) (*Conn, error)

Upgrade http upgrade to websocket protocol

Directories

Path Synopsis
autobahn
client command
reporter command
server command
examples
chatroom command
client command
echo command
wss command

Jump to

Keyboard shortcuts

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