Skip to content

Commit

Permalink
Merge pull request #8 from lxzan/tcp-upgrader
Browse files Browse the repository at this point in the history
WebSocket over TCP
  • Loading branch information
lxzan authored Apr 27, 2023
2 parents fc08683 + 11e9a2f commit 3a204b8
Show file tree
Hide file tree
Showing 8 changed files with 365 additions and 106 deletions.
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

0 comments on commit 3a204b8

Please sign in to comment.