gobwas / ws

Tiny WebSocket library for Go.
MIT License
6.1k stars 373 forks source link

I encountered a problem of automatic disconnection after 30 seconds #202

Closed TianLiangZhou closed 3 months ago

TianLiangZhou commented 3 months ago

If my client program sends OpText and OpBinary data regularly, it will not disconnect. But I send OpPing regularly, and the server program also returns OpPong. It will automatically disconnect after 30 seconds, and 30 seconds is always fixed.

// server

go func() {
        defer func() {
            if err := recover(); err != nil {
                buf := make([]byte, 1<<16)
                stackSize := runtime.Stack(buf, true)
                logger.Errorf("client occur panic, err: %v, stack: %s", err, string(buf[0:stackSize]))
            }
            logger.Infof("Client [%s] HUP disconnected", client.id)
            if wh.srv.OnDisconnect != nil {
                go wh.srv.OnDisconnect(client)
            }
            // _ = w.epoller.del(client.fd)
            wh.srv.Lock()
            delete(wh.srv.clients, client.ID())
            wh.srv.Unlock()
            _ = client.conn.Close()
        }()
        for {
            // set SetReadDeadline
            err := conn.SetReadDeadline(time.Time{})
            if err != nil {
                logger.Errorf("SetReadDeadline failed: %s", err)
                // do something else, for example create new conn
                return
            }
            messages, err := wsutil.ReadClientMessage(client.conn, []wsutil.Message{})
            if err != nil { // Read error
                logger.Infof("Websocket read error from client [%s] - %s", client.ID(), err)
                return
            }
            for _, msg := range messages {
                switch msg.OpCode {
                case ws.OpText, ws.OpBinary:
                    if wh.srv.OnData == nil {
                        logger.Errorf("websocket handler on data handler not setting, body: %s", string(msg.Payload))
                        return
                    }
                    if err = wh.srv.OnData(client, msg.Payload); err != nil {
                        logger.Errorf("websocket handler data failure, body: %s err: %v", string(msg.Payload), err)
                    }
                case ws.OpClose: // Close
                    logger.Infof("receive client closed")
                case ws.OpPing: // Ping
                    logger.Infof("receive client ping control message")
                    if err := wsutil.WriteServerMessage(conn, ws.OpPong, nil); err != nil {
                        logger.Errorf("send pong control message failure, err: %v", err)
                    }
                case ws.OpContinuation: // Continuation
                    logger.Infof("receive client continuation")
                case ws.OpPong: // Pong
                    logger.Infof("receive pong....")
                default: // WTF -_-!
                    logger.Errorf("receive error message from client: %s, op: %v", string(msg.Payload), msg.OpCode)
                }

            }
        }

    }()

// client

go func() {
        defer func(conn net.Conn) {
            logger.Infof("close conn")
            err := conn.Close()
            if err != nil {
                logger.Infof("close conn error: %+v", err)
            }
        }(conn)
        for {
            err := conn.SetReadDeadline(time.Time{})
            messages, err := wsutil.ReadServerMessage(conn, []wsutil.Message{})
            if err != nil {
                logger.Infof("read error: %+v", err)
                return
            }
            logger.Infof("on receive message: %+v", messages)
            for _, msg := range messages {
                switch msg.OpCode {
                case ws.OpText, ws.OpBinary:
                    if client.OnData != nil {
                        err := client.OnData(client, msg.Payload)
                        if err != nil {
                            logger.Infof("on receive data error: %s", err)
                        }
                    }
                case ws.OpPing:
                    logger.Infof("on receive ping message")
                    _ = wsutil.WriteClientMessage(conn, ws.OpPong, nil)
                case ws.OpPong:
                    logger.Infof("on receive pong message")
                case ws.OpClose:
                    logger.Infof("on receive close message")
                    return
                }
            }

        }
    }()

    go func() {
        ticker := time.NewTicker(pingPeriod)
        defer func() {
            ticker.Stop()
            _ = conn.Close()
        }()
        for {
            select {
            case <-ticker.C:
                logger.Infof("ticker .......")
                // _ = conn.SetWriteDeadline(time.Now().Add(writeWait))
                             //cmd := message.GenerateSimpleCommand(
                //  message.CommandPing,
                //)
                //err := wsutil.WriteClientMessage(conn, ws.OpText, cmd.Bytes())
                err := wsutil.WriteClientMessage(conn, ws.OpPing, nil)
                if err != nil {
                    logger.Infof("ticker write  ....... %+v", err)
                    break
                }
            }
        }
    }()
cristaloleg commented 3 months ago

What was the problem?

TianLiangZhou commented 3 months ago

@cristaloleg Other places are cleaned regularly 😭