- Event API
- Broadcast
- Dial via Proxy
- IO Multiplexing
- Concurrent Write
- Zero Allocs Read/Write (Compression Disabled)
- Passes WebSocket autobahn-testsuite
- The errors returned by the gws.Conn export methods are ignored, and are handled internally
- Transferring large files with gws tends to block the connection
go get -v github.com/lxzan/gws@latest
type Event interface {
OnOpen(socket *Conn)
OnClose(socket *Conn, err error)
OnPing(socket *Conn, payload []byte)
OnPong(socket *Conn, payload []byte)
OnMessage(socket *Conn, message *Message)
}
package main
import "github.com/lxzan/gws"
func main() {
gws.NewServer(new(gws.BuiltinEventHandler), nil).Run(":6666")
}
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) OnClose(socket *gws.Conn, err error) {}
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()
}
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)
}
}
- server
package main
import (
"log"
"github.com/lxzan/gws"
kcp "github.com/xtaci/kcp-go"
)
func main() {
listener, err := kcp.Listen(":6666")
if err != nil {
log.Println(err.Error())
return
}
app := gws.NewServer(&gws.BuiltinEventHandler{}, nil)
app.RunListener(listener)
}
- client
package main
import (
"github.com/lxzan/gws"
kcp "github.com/xtaci/kcp-go"
"log"
)
func main() {
conn, err := kcp.Dial("127.0.0.1:6666")
if err != nil {
log.Println(err.Error())
return
}
app, _, err := gws.NewClientFromConn(&gws.BuiltinEventHandler{}, nil, conn)
if err != nil {
log.Println(err.Error())
return
}
app.ReadLoop()
}
package main
import (
"crypto/tls"
"github.com/lxzan/gws"
"golang.org/x/net/proxy"
"log"
)
func main() {
socket, _, err := gws.NewClient(new(gws.BuiltinEventHandler), &gws.ClientOption{
Addr: "wss://example.com/connect",
TlsConfig: &tls.Config{InsecureSkipVerify: true},
NewDialer: func() (gws.Dialer, error) {
return proxy.SOCKS5("tcp", "127.0.0.1:1080", nil, nil)
},
})
if err != nil {
log.Println(err.Error())
return
}
socket.ReadLoop()
}
func Broadcast(conns []*gws.Conn, opcode gws.Opcode, payload []byte) {
var b = gws.NewBroadcaster(opcode, payload)
defer b.Release()
for _, item := range conns {
_ = b.Broadcast(item)
}
}
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
- GOMAXPROCS = 4
- Connection = 1000
- Compress Disabled
$ go test -benchmem -run=^$ -bench ^(BenchmarkConn_WriteMessage|BenchmarkConn_ReadMessage)$ github.com/lxzan/gws
goos: darwin
goarch: arm64
pkg: github.com/lxzan/gws
BenchmarkConn_WriteMessage/compress_disabled-8 8713082 138.0 ns/op 0 B/op 0 allocs/op
BenchmarkConn_WriteMessage/compress_enabled-8 144266 8066 ns/op 235 B/op 0 allocs/op
BenchmarkConn_ReadMessage/compress_disabled-8 11608689 102.8 ns/op 12 B/op 0 allocs/op
BenchmarkConn_ReadMessage/compress_enabled-8 435176 2498 ns/op 98 B/op 1 allocs/op
微信二维码在讨论区不定时更新
The following project had particular influence on gws's design.