Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebSocket over TCP #8

Merged
merged 9 commits into from
Apr 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 61 additions & 50 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,23 @@
[10]: https://goreportcard.com/report/github.com/lxzan/gws

- [gws](#gws)
- [Install](#install)
- [Interface](#interface)
- [Examples](#examples)
- [Quick Start](#quick-start)
- [HeartBeat](#heartbeat)
- [Broadcast](#broadcast)
- [TLS](#tls)
- [Autobahn Test](#autobahn-test)
- [Benchmark](#benchmark)
- [IOPS](#iops)
- [Latency](#latency)
- [CPU](#cpu)
- [Acknowledgments](#acknowledgments)

- [event-driven go websocket server](#event-driven-go-websocket-server)
- [Highlight](#highlight)
- [Install](#install)
- [Event](#event)
- [Examples](#examples)
- [Quick Start](#quick-start)
- [Advanced](#advanced)
- [Autobahn Test](#autobahn-test)
- [Benchmark](#benchmark)
- [Acknowledgments](#acknowledgments)

#### Highlight

- No dependency
- 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, 30% lower memory usage
- Fully passes the WebSocket [autobahn-testsuite](https://github.com/crossbario/autobahn-testsuite)

#### Install
Expand All @@ -53,7 +50,7 @@
go get -v github.com/lxzan/gws@latest
```

#### Interface
#### Event

```go
type Event interface {
Expand All @@ -69,12 +66,12 @@ type Event interface {
#### Examples

- [chat room](examples/chatroom/main.go)
- [echo](examples/server/server.go)
- [echo](examples/wss-server/server.go)

#### Quick Start

- server

```go
package main

Expand Down Expand Up @@ -150,36 +147,23 @@ func (c *WebSocket) OnMessage(socket *gws.Conn, message *gws.Message) {
}
```

#### HeartBeat
```go
const PingInterval = 5 * time.Second
#### Advanced

type Websocket struct {
gws.BuiltinEventHandler
}
- WebSocket over TCP

func (w Websocket) OnOpen(socket *gws.Conn) {
_ = socket.SetDeadline(time.Now().Add(3 * PingInterval))
}

func (w Websocket) OnPing(socket *gws.Conn, payload []byte) {
_ = socket.WritePong(nil)
_ = socket.SetDeadline(time.Now().Add(3 * PingInterval))
}
```

#### Broadcast
```go
func Broadcast(conns []*gws.Conn, opcode gws.Opcode, payload []byte) {
for _, item := range conns {
_ = item.WriteAsync(opcode, payload)
// compared to hijacking http, handshake is faster and more memory efficient
func main() {
srv := gws.NewServer(new(Websocket), nil)
if err := srv.Run(":3000"); err != nil {
log.Fatalln(err.Error())
}
}
```

#### TLS

- Gin
```go

package main

import (
Expand All @@ -189,22 +173,49 @@ import (

func main() {
app := gin.New()
upgrader := gws.NewUpgrader(handler := new(WebSocket), nil)
upgrader := gws.NewUpgrader(new(WebSocket), nil)
app.GET("/connect", func(ctx *gin.Context) {
socket, err := upgrader.Accept(ctx.Writer, ctx.Request)
if err != nil {
return
}
upgrader.Listen(socket)
go upgrader.Listen(socket)
})
cert := "server.crt"
key := "server.key"
if err := app.RunTLS(":8443", cert, key); err != nil {
if err := app.Run(":8080"); err != nil {
panic(err)
}
}
```

- HeartBeat

```go
const PingInterval = 5 * time.Second

type Websocket struct {
gws.BuiltinEventHandler
}

func (w Websocket) OnOpen(socket *gws.Conn) {
_ = socket.SetDeadline(time.Now().Add(3 * PingInterval))
}

func (w Websocket) OnPing(socket *gws.Conn, payload []byte) {
_ = socket.WritePong(nil)
_ = socket.SetDeadline(time.Now().Add(3 * PingInterval))
}
```

- Broadcast

```go
func Broadcast(conns []*gws.Conn, opcode gws.Opcode, payload []byte) {
for _, item := range conns {
_ = item.WriteAsync(opcode, payload)
}
}
```

#### Autobahn Test

```bash
Expand All @@ -217,28 +228,28 @@ docker run -it --rm \
wstest -m fuzzingclient -s /config/fuzzingclient.json
```

#### Benchmark
#### Benchmark

- Machine: `Ubuntu 20.04LTS VM (4C8T)`

##### IOPS
- IOPS

```
// ${message_num} depends on the maximum load capacity of each package
tcpkali -c 1000 --connect-rate 500 -r ${message_num} -T 300s -f assets/1K.txt --ws 127.0.0.1:${port}/connect
```

![rps](assets/performance.png)
![iops](assets/performance.png)

##### Latency
- Latency

```
tcpkali -c 1000 --connect-rate 500 -r 100 -T 300s -f assets/1K.txt --ws 127.0.0.1:${port}/connect
```

![gws-c1000-m100](assets/latency.png)
![latency](assets/latency.png)

##### CPU
- CPU

```
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
Expand Down
2 changes: 1 addition & 1 deletion compress.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func (c *compressor) Close() {

// Compress 压缩
func (c *compressor) Compress(content *bytes.Buffer) (*bytes.Buffer, error) {
c.buffer = _bpool.Get(content.Len())
c.buffer = _bpool.Get(content.Len() / 3)
c.fw.Reset(c.buffer)
if err := internal.WriteN(c.fw, content.Bytes(), content.Len()); err != nil {
return nil, err
Expand Down
33 changes: 0 additions & 33 deletions examples/server/server.go

This file was deleted.

20 changes: 20 additions & 0 deletions examples/wss-server/cert/server.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDQjCCAioCCQCWPZ0wOmVAQzANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJj
bjEOMAwGA1UECAwFaHVuYW4xETAPBgNVBAcMCGNoYW5nc2hhMQ8wDQYDVQQKDAZ3
ZWJ1bGwxIDAeBgkqhkiG9w0BCQEWEWx4emFuQGZveG1haWwuY29tMB4XDTIzMDIw
MjA4MTE1MloXDTIzMDMwNDA4MTE1MlowYzELMAkGA1UEBhMCY24xDjAMBgNVBAgM
BWh1bmFuMREwDwYDVQQHDAhjaGFuZ3NoYTEPMA0GA1UECgwGd2VidWxsMSAwHgYJ
KoZIhvcNAQkBFhFseHphbkBmb3htYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBAMvDXJ52ijEkpmBrmwwhresG1UzI3tUHbZ8xGkUSJtkKovFS
T8m3gNbkFsF0uFMsdRmdjVE6SZ61sW41EwNYpuVugBbeLdnKHJ374EB1YybTdva3
JBWfa9kC1v0VgtUyeqQ0NRMbkSwRc0wEdottClRA6vQhS5uBZOlqgbLC4xqmRIkd
/85VpHT4M5exoo/09S+Y7K6/XxE/iCqX/ylApuiuyddvUjxv9zSwCpVjRRaeHBLu
PW2XBx3pD5knkr7cVmlE17LWfJK/6XmH++Kbl9stKNVLblSCMKQnx5ETtPXDnxtQ
YQa6/rzEv1kuLnXr0wFGTVwKfI0f+fUqlIyhFusCAwEAATANBgkqhkiG9w0BAQsF
AAOCAQEAiEGqckyHQvAgmx3iws7hRo6T8FTJK1t6daxHD/HX7zZzkJUjQOQZhao2
KCfVxWfJ9YJAVL8S8wcfJiRIZ6Kk5k1Z6rhmsa8sNGe25BVdL9Yjv5LvpC5V8dpd
MSNVfxj7iO1rOjJbx+xpleHgksSnmkKp4mbYqyLuQQOMztlbkODDSt3AYgTfT0pN
DjqR9ylDq0Euk4xsCR7UDaRVfx8LXaFPOaXJmEyBRS+x/PV+qjB7uyDINe9rmq7d
hk6xLDnRauTgBXTO8n/6W4yDHjTO6Oc82j18D7SZKnDcyhmuPAe0k3wPAP3gcv4d
8X/c3ErSIfsSgCv4vGO1hDZtFevMFQ==
-----END CERTIFICATE-----
27 changes: 27 additions & 0 deletions examples/wss-server/cert/server.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAy8NcnnaKMSSmYGubDCGt6wbVTMje1QdtnzEaRRIm2Qqi8VJP
ybeA1uQWwXS4Uyx1GZ2NUTpJnrWxbjUTA1im5W6AFt4t2cocnfvgQHVjJtN29rck
FZ9r2QLW/RWC1TJ6pDQ1ExuRLBFzTAR2i20KVEDq9CFLm4Fk6WqBssLjGqZEiR3/
zlWkdPgzl7Gij/T1L5jsrr9fET+IKpf/KUCm6K7J129SPG/3NLAKlWNFFp4cEu49
bZcHHekPmSeSvtxWaUTXstZ8kr/peYf74puX2y0o1UtuVIIwpCfHkRO09cOfG1Bh
Brr+vMS/WS4udevTAUZNXAp8jR/59SqUjKEW6wIDAQABAoIBAAkMLaqjnNM9y11w
qbvKKwzKiuIT8UEISWfOKFp0yeDNRhrtCrnsOf5Qtmn0otKeoLx4cHPw4J/5dV1t
YeC+KPr7AxkA2IxZGxgwvO5fx/1LWnbt/t4u6/0A0Ub/k4Wf7i4oW0rz6GhKhhIU
CKd8Of8vur8IU3EeN7rdjdDAMpFWSEeU5Abp3uwk7wZ3Mh5207fb2ZimWkk6w/yh
ukh1AbkCbidAZcJjIkSJm0y1qLVYWXddXeAr1oqthpg9Ni6By+uv8l9pVuUTRB/h
0YelF3IOgthsqF3ruLa7SDfyor84fvz5N65aXiTDzh5E270KRWILLVWerQ6GMReS
LZo4LzECgYEA+9fnCncDw7Xb6wy9/zNF7YmDZslTSnZtN3B562Ul0RzgBkD/zerX
2X+ReEaKK0WbT2kpfZVtld8vItU9AvE0IaX/T/PH2vhzUX7YTR3sy+pouZVqknfs
fTibdkctNH6JYRWKKTqziQ3CB1+n9k3EmnAW97U6spnX9yP4A85ncQcCgYEAzyBP
GOYyOOTGfslkixyOdskil0VRFB0aspsAFgl9WTnChYHNNOYDYofpoFsUcg1xZcKt
8YBj+CZDk+8KMd+a+PykLvNWkNn6JVaI2b7/daBdQF2oIoppIYWWnV1a6pK91K2b
lOjhPGbgrv63e7qJ447ZLR04xL4JO34KK7c0xf0CgYA8ILURw46LiHzkJpuqNXSp
nN5ZiRGwwoua4+jH8dNQlONLuvMYF8zHKfTyUW0lna3SDNqSGD4cvTeEyoKRlEMN
EZPeY+bAefcWawF7j/x8RxAP1wOlyqoUz5IWcDHzyY5FNp5Q76nzu+wqVIVGghTB
hqeFJqjIAYEViWnqhroduwKBgGEmI1NnbTj4PQ3swibpjvyyHHJdRcksWYBaaTDs
5Ik8KTvbhPeBxyao3vCip7VTDtQO+u0iLkGPGPVDEkIQQiz0usF2yyyHwOdQbkHL
byCpxfNRunHw/5yEfv9ycRlDGJyvjSDMFhDxpAMQX9k9xj0gnEanxR+qjsQDuxuE
A9G1AoGBAIqjCSvJoHXJKAQwt2hUgvrWN/91QJ+REryfwT5iQmweZXjGt7j4wExr
9M47OSBIbRVyXrhv3U2oi/2U0efR3Xe8444YkmX8Zr7m1ibdXyybQh9VtdB2DaXq
4uzx71k5Nu6EyUyKO4XfogBzCu/1GXpaqphulLUHHAXf/U2vBPwg
-----END RSA PRIVATE KEY-----
43 changes: 43 additions & 0 deletions examples/wss-server/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package main

import (
"flag"
"github.com/lxzan/gws"
"log"
"path/filepath"
)

var dir string

func init() {
flag.StringVar(&dir, "d", "", "cert directory")
flag.Parse()

d, err := filepath.Abs(dir)
if err != nil {
log.Printf(err.Error())
return
}
dir = d
}

func main() {
srv := gws.NewServer(new(Websocket), nil)

if err := srv.RunTLS(":3000", dir+"/server.crt", dir+"/server.pem"); err != nil {
log.Panicln(err.Error())
}
}

type Websocket struct {
gws.BuiltinEventHandler
}

func (c *Websocket) OnPing(socket *gws.Conn, payload []byte) {
socket.WritePong(payload)
}

func (c *Websocket) OnMessage(socket *gws.Conn, message *gws.Message) {
defer message.Close()
_ = socket.WriteMessage(message.Opcode, message.Bytes())
}
Loading