- fully passes the websocket autobahn-testsuite
- thread safety guarantees for writing messages
- high iops and low latency, low cpu usage
- io multiplexing support, concurrent message processing and asynchronous non-blocking message writing
- fast upgrade from tcp to websocket, dramatically reduce memory usage
- create client via proxy
- 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"
"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()
}
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) {
for _, item := range conns {
_ = item.WriteAsync(opcode, payload)
}
}
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
- Fedora 38 Workstation (Linux 6.2 Kernel)
- Connection = 1000
- GOMAXPROCS = 2
微信二维码在讨论区不定时更新
The following project had particular influence on gws's design.