neffos icon indicating copy to clipboard operation
neffos copied to clipboard

How to monitor client disconnect / close connection?

Open baagod opened this issue 4 years ago • 11 comments

I want to do something when closing, or interrupting the client connection, such as cleaning up. I call conn.Close() on ws.OnConnect{} without triggering ws.OnDisconnect{}. what should I do?

package main

import (
    "fmt"
    "github.com/kataras/iris/v12"
    "github.com/kataras/iris/v12/websocket"
    "github.com/kataras/neffos"
)

func main() {
    app := iris.Default()
    
    ws := neffos.New(websocket.DefaultGorillaUpgrader, neffos.Namespaces{
    	neffos.OnNativeMessage: neffos.Events{
    	    "login": func(ns *neffos.NSConn, msg neffos.Message) error {
    	        return nil
    	    },
    	},
    })
    
    ws.OnConnect = func(conn *neffos.Conn) error {
        allowConnect = true
        // some operations...
        if !allowConnect {
            conn.Close()
        }
    	return nil
    }
    
    ws.OnDisconnect = func(c *neffos.Conn) {
        // No shutdown signal is received and the method body is not triggered.
    	fmt.Println("OnDisconnect")
    }
    
    app.Get("/login", websocket.Handler(ws))
    app.Logger().Fatal(app.Listen(":8080"))
}

baagod avatar May 12 '20 07:05 baagod

@hjzCy You can't close the connection immediately from the ws.OnConnect event. If you want to close the connection from OnConnect you should return a NON-NIL ERROR. Also, there is a note on the code at:

https://github.com/kataras/neffos/blob/f1431864185db0b334b82e3817eb03bd957f9fda/server.go#L174-L179

We can change that if you want by an option(in order to not break existing behavior).

However, if you wait 1 second, it will be fired, although there is no logic on closing the connection without any checks when it's just connected (that's why we have the above check in the code too):

  ws.OnConnect = func(conn *neffos.Conn) error {
        time.Sleep(time.Second)
    	conn.Close()
    	return nil
    }

kataras avatar May 18 '20 16:05 kataras

OK @hjzCy, a FireDisconnectAlways option was added.

$ go get github.com/kataras/[email protected]
# or go.mod:
# require github.com/kataras/neffos v0.0.16
  ws := neffos.New([...])
  ws.FireDisconnectAlways = true // <---HERE

  ws.OnConnect = func(conn *neffos.Conn) error {
    	conn.Close()
    	return nil
}

ws.OnDisconnect = func(conn *neffos.Conn) {
    log.Printf("[%s] disconnected from the server.", c)
}

kataras avatar May 18 '20 16:05 kataras

@kataras Thank you!very perfect!

baagod avatar May 19 '20 04:05 baagod

@kataras After setting FireDisconnectAlways = true, if the client suddenly disconnects, any data you want to get from the conn of the OnDisconnect{} method is invalid. And may cause the program to crash:

ws.OnDisconnect = apis.Logout

func Logout(conn *neffos.Conn) {
    ctx := websocket.GetContext(conn)
    token := strings.ReplaceAll(ctx.URLParam("token"), " ", "+")
    decode := aes.CBCDecrypt(token)

    // If `FireDisconnectAlways = false` then data can be obtained normally
    fmt.Println("token:", token)
    fmt.Println("decode:", decode)

    // If `FireDisconnectAlways = false`, it will execute normally.
    // Otherwise, the program will crash directly here ( Out of range )!!
    username := decode[:strings.Index(decode, ", ")]

    if client, ok := loginClients[username]; ok {
        if client.topStatus {
            log.Println("顶号断开连接,不删除用户数据。将顶号状态设置为 false。")
            client.topStatus = false
        } else {
            log.Println("异常、或正常断开连接,删除用户数据。")
            delete(loginClients, username)
        }
    }
}

baagod avatar May 19 '20 17:05 baagod

The question now is that after the client disconnects, the data returned from conn is invalid. How can I still get the data from theOnDisconnect {}method after the client disconnects? E.g,Want to get the url parameter of this connection:

ctx := websocket.GetContext(conn)
fmt.Println("token:", ctx.URLParam("token")) // get failed, token is ""

baagod avatar May 19 '20 17:05 baagod

@hjzCy The connection is already closed and removed from the server when ws.OnDisconnect is fired (see https://github.com/kataras/neffos/blob/15e3a17ca8e37b9e793fd59811e7fb42a30ac5ee/conn.go#L1039). Why you don't use the neffos.OnNamespaceDisconnect event instead? It will be called on your conn.Close() too.

kataras avatar May 19 '20 18:05 kataras

@kataras I cannot use neffos.OnNamespaceDisconnect because I need to use neffos.OnNativeMessage and my python client cannot connect to the namespace.

baagod avatar May 19 '20 18:05 baagod

@kataras The two problems above are that I didn't manually call conn.Close (), but when the client disconnected, ws.OnDisconnect` received the signal automatically.

baagod avatar May 19 '20 18:05 baagod

没有手动调用 conn.Close(),而是客户端由于某种原因断开了连接,此时 ws.OnDisconnect 接收到信号:

ws.OnDisconnect = apis.Logout

func Logout(conn *neffos.Conn) {
    ctx := websocket.GetContext(conn)
    // FireDisconnectAlways = false can get the result normally, otherwise "".
    fmt.Println("token:", ctx.URLParam("token"))
}

Cannot get url parameter data.

baagod avatar May 19 '20 18:05 baagod

Well, I found that even if FireDisconnectAlways = true, sometimes when the client is suddenly disconnected, the conn data stored in the ws.OnDisconnect () method cannot be obtained.

A suggestion: Is it possible to wait until the body of the ws.OnDisconnect() method is executed, and then disconnect from the conn, so that you can do some things by getting the data of conn when disconnecting.

baagod avatar May 20 '20 18:05 baagod

Or add a signal: ws.onWillDisconnect() ?

baagod avatar May 21 '20 04:05 baagod