- zero dependency, not use channel but event driven
- zero extra goroutine to manage connection
- zero error to read/write operation, errors have been handled appropriately
- built-in concurrent_map implementation
- fully passes the WebSocket autobahn-testsuite
- It's designed for api server, do not write big message.
- WebSocket events are emitted synchronously, manage goroutines yourself.
- Write operations are synchronous blocking, for broadcast scenarios, special attention is needed.
- machine: Ubuntu 20.04LTS VM (4C8T)
- client: tcpkali
- payload: 2.34KiB
Server | Connection | Send Speed (msg/s) | T (s) | Download / Upload Bandwidth (Mbps) |
---|---|---|---|---|
gws | 200 | 4000 | 30 | 10916.094↓ 10951.587↑ |
gorilla | 200 | 4000 | 30 | 4344.222↓ 4380.711↑ |
gws | 2000 | 300 | 30 | 7941.090↓ 7951.117↑ |
gorilla | 2000 | 300 | 30 | 4706.938↓ 4715.744↑ |
gws | 5000 | 60 | 30 | 5891.151↓ 5908.599↑ |
gorilla | 5000 | 60 | 30 | - |
gws | 10000 | 10 | 60 | 1980.124↓ 1977.561↑ |
gorilla | 10000 | 10 | 60 | 1972.556↓ 1979.981↑ |
gws | 10000 | 20 | 60 | 3952.788↓ 3959.341↑ |
gorilla | 10000 | 20 | 60 | - |
-
means exception
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)
}
go get -v github.com/lxzan/gws@latest
package main
import (
"fmt"
"github.com/lxzan/gws"
"net/http"
)
func main() {
var upgrader = gws.NewUpgrader(func(c *gws.Upgrader) {
c.CompressEnabled = true
c.CheckTextEncoding = true
c.MaxContentLength = 32 * 1024 * 1024
c.EventHandler = new(WebSocket)
})
http.HandleFunc("/connect", func(writer http.ResponseWriter, request *http.Request) {
socket, err := upgrader.Accept(writer, request)
if err != nil {
return
}
socket.Listen()
})
_ = http.ListenAndServe(":3000", nil)
}
type WebSocket struct{}
func (c *WebSocket) OnClose(socket *gws.Conn, code uint16, reason []byte) {
fmt.Printf("onclose: code=%d, payload=%s\n", code, string(reason))
}
func (c *WebSocket) OnError(socket *gws.Conn, err error) {
fmt.Printf("onerror: err=%s\n", err.Error())
}
func (c *WebSocket) OnOpen(socket *gws.Conn) {
println("connected")
}
func (c *WebSocket) OnPing(socket *gws.Conn, payload []byte) {
fmt.Printf("onping: payload=%s\n", string(payload))
socket.WritePong(payload)
}
func (c *WebSocket) OnPong(socket *gws.Conn, payload []byte) {}
func (c *WebSocket) OnMessage(socket *gws.Conn, message *gws.Message) {
socket.WriteMessage(message.Typ(), message.Bytes())
message.Close()
}
package main
import (
"github.com/gin-gonic/gin"
"github.com/lxzan/gws"
)
func main() {
app := gin.New()
handler := new(WebSocket)
upgrader := gws.NewUpgrader(gws.WithEventHandler(handler))
app.GET("/connect", func(ctx *gin.Context) {
socket, err := upgrader.Accept(ctx.Writer, ctx.Request)
if err != nil {
return
}
upgrader.Listen(socket)
})
cert := "server.crt"
key := "server.key"
if err := app.RunTLS(":8443", cert, key); err != nil {
panic(err)
}
}
cd examples/testsuite
mkdir reports
docker run -it --rm \
-v ${PWD}/config:/config \
-v ${PWD}/reports:/reports \
crossbario/autobahn-testsuite \
wstest -m fuzzingclient -s /config/fuzzingclient.json