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

EOF error on ws.Upgrade #153

Open
piotrpersona opened this issue Aug 11, 2022 · 8 comments
Open

EOF error on ws.Upgrade #153

piotrpersona opened this issue Aug 11, 2022 · 8 comments

Comments

@piotrpersona
Copy link

I receive EOF error on ws.Upgrade during high load on a server. I took code from example and run ~2k concurrent connections to websocket server. Another thing is that I pass JWT token in URL.
It seems that readLine inside Upgrade is causing error: https://github.com/gobwas/ws/blob/master/server.go#L452
and then error is returned from here: https://github.com/gobwas/ws/blob/master/util.go#L183

Example code to reproduce the issue:
Server:

package main

import (
	"log"
	"net"

	"github.com/gobwas/ws"
)

func main() {
	ln, err := net.Listen("tcp", ":8080")
	if err != nil {
		log.Fatal(err)
	}

	for {
		conn, err := ln.Accept()
		if err != nil {
			log.Fatal("accept err", err)
			continue
		}
		_, err = ws.Upgrade(conn)
		if err != nil {
			log.Fatal("upgrade err", err)
			continue
		}
	}
}

client:

package main

import (
	"fmt"
	"log"
	"sync"

	"github.com/gorilla/websocket"
)

func main() {
	for {
		var wg sync.WaitGroup
		for i := 0; i < 2000; i++ {
			wg.Add(1)
			go func(i int) {
				defer wg.Done()
				url := "ws://localhost:8080/ws?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
				_, _, err := websocket.DefaultDialer.Dial(url, nil)
				if err != nil {
					err = fmt.Errorf("cannot dial, err: %w", err)
					log.Println(err)
					return
				}
				return
			}(i)
		}
		wg.Wait()
		fmt.Println("done")
	}
}
@Jekinnnnnn
Copy link

This issue maybe relates to tcp config,check somaxconn size and fd size in your environment.

@piotrpersona
Copy link
Author

somaxconn:
kern.ipc.somaxconn: 18000

$ ulimit -a
Maximum size of core files created                           (kB, -c) 0
Maximum size of a process’s data segment                     (kB, -d) unlimited
Maximum size of files created by the shell                   (kB, -f) unlimited
Maximum size that may be locked into memory                  (kB, -l) unlimited
Maximum resident set size                                    (kB, -m) unlimited
Maximum number of open file descriptors                          (-n) 8000
Maximum stack size                                           (kB, -s) 8176
Maximum amount of cpu time in seconds                   (seconds, -t) unlimited
Maximum number of processes available to a single user           (-u) 2666
Maximum amount of virtual memory available to the shell      (kB, -v) unlimited

It seems that messages are cut off when reading from connection, for example:

...Upgrade\r\nSec-WebSocket-Key: 122CV3QQHRFoIKR44OzQAA==\r\nSec-WebSocket-Version: 13\r\nUpgrade:"

Each message is 512 bytes-long.

Can I ask for a guide, what are the other settings I should check?
I'm out of ideas right now

@Jekinnnnnn
Copy link

ln, err := net.Listen("tcp", ":8080")
	if err != nil {
		log.Fatal(err)
	}

Try to use http server,like Readme shows,because websocket bases on http 1.1or above version not tcp.
If you want to get token before Upgrade,I give a reference below

type Token struct {
	Token string `json:"token" bingding:"required"`
}
var param Token
if err := c.ShouldBindJSON(&param); err != nil {
	return
}
log.Println("token=", param.Token)

c in code is *gin.Context

@piotrpersona
Copy link
Author

Thank you, I will give it a try.

The server I posted above is from README "The lower-level example without wsutil" example.
Http 1.1 uses tcp behind the scenes. According to this article: https://www.freecodecamp.org/news/million-websockets-and-go-cc58418460bb/ it is fine to use net.Listen and Accept along with gobwas/ws package (without wsutil).

I wonder if the example from README is sufficient to handle mentioned a million websockets connections.

@Jekinnnnnn
Copy link

Jekinnnnnn commented Aug 16, 2022

Sorry,I didn't notice that example,maybe lower-level only supports pure url without parameters?

@piotrpersona
Copy link
Author

Unfortunately, I think ws.Upgrade has some issues. Here is simpler example.

Server:

package main

import (
	"fmt"
	"net"

	"github.com/gobwas/ws"
)

func main() {
	ln, err := net.Listen("tcp", ":8111")
	if err != nil {
		panic(err)
	}

	for {
		conn, err := ln.Accept()
		if err != nil {
			fmt.Println("accept err", err)
			continue
		}

		_, err = ws.Upgrade(conn)
		if err != nil {
			fmt.Println("upgrade", err)
		}
	}
}

client:

package main

import (
	"context"
	"fmt"
	"sync"
	"time"

	"github.com/gorilla/websocket"
)

func main() {
	var wg sync.WaitGroup
	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
			defer cancel()
			_, _, err := websocket.DefaultDialer.DialContext(ctx, "ws://localhost:8111", nil)
			if err != nil {
				fmt.Println(err)
			}
		}()
	}
	wg.Wait()
}

There are no params in URL, but I receive upgrade EOF. For some reason, this method returns EOF: https://github.com/gobwas/ws/blob/master/util.go#L169.

This is non-deterministic. I have to run client code few times to observe EOF error

@linguanyuanA
Copy link

linguanyuanA commented May 9, 2023

这个情况确实是必现的。因为你的client在链接建立后,就自己销毁了。 相当于客户端主动断开链接。
server upgrade 失败 EOF。这个应该是合理的。就是会打印日志

@let4be
Copy link

let4be commented Jun 15, 2023

I'm having similar issue - reading from web socket server returns EOF... consistently
(first read after ws.Upgrade)
Looks like this lib is unusable in the current form, should'a went with gorilla instead... but it's archived now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants