doumei1772
2018-11-21 16:22
浏览 661
已采纳

关闭已在服务器端发送,但客户端仍保持连接

I have an application that communicates over an API and a websocket. The websocket is used to publish updated userdata to the client if it is changed in the database - this works perfectly, except for the case that the websocket does not receive any data in some cases. After a few seconds, the websocket starts working again.

The server log (first, the websocket does not work and starts working again)

msg="Failed to write data to Websocket: websocket: close sent"

msg="Sending Ping Message to Client"

msg="Failed to write ping message to Websocket: websocket: close sent"

msg="Sending Ping Message to Client"

msg="Sending Ping Message to Client"

msg="Sending Ping Message to Client"

msg="Sending Ping Message to Client"

The client-side code:

<html>
<body>
<p id="data"></p>
</body>
<script>
var ws = new WebSocket("wss://example.com/ws");

function unloadPage() {
    toggleLoader();
    ws.onclose = function () {};
    ws.close();
}

ws.onopen = function () {
    ws.send('Ping');
};
ws.onerror = function (error) {
    console.log('WebSocket Error ' + error);
    var d = document.getElementById("data");
    d.innerHTML += "<tr><td>Failed to connect to Server.</td></tr>"
};
ws.onmessage = function (e) {
    console.log(e);
    var data = e.data;
    var d = document.getElementById("data");
    var parsedjson = JSON.parse(data);
    d.innerHTML = "";
    for (var i = 0; i < parsedjson.length; i++) {
        d.innerHTML += parsedjson;
    }
};
ws.onclose = function () {
    console.log("Websocket has been closed");
};
window.addEventListener("beforeunload", unloadPage);
</script>
</html>

The Go Code (routed through gorilla mux):

var (
    upgrader = websocket.Upgrader{
        ReadBufferSize:  1024,
        WriteBufferSize: 1024,
        CheckOrigin:     func(r *http.Request) bool { return true },
    }
    pingPeriod = (pongPeriod * 9) / 10
    pongPeriod = 60 * time.Second
    writeWait  = 10 * time.Second
)


func PingResponse(ws *websocket.Conn) {
    conf := storage.GetConfig()
    defer ws.Close()
    ws.SetReadLimit(512)
    ws.SetReadDeadline(time.Now().Add(pongPeriod))
    ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongPeriod)); return nil })
    for {
        _, _, err := ws.ReadMessage()
        if err != nil {
            if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
                conf.Log.Debugf("Websocket Ping Read Failed: %v", err)
            }

            return
        } else {
            conf.Log.Debugf("Received message from Websocket client")
        }
    }
}

func ServeAllUsersWebsocket(datachan chan *[]storage.UserResponse) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        conf := storage.GetConfig()
        ws, err := upgrader.Upgrade(w, r, nil)
        if err != nil {
            conf.Log.Debugf("Failed to upgrade data to Websocket: %v", err)
            return
        }

        go allUserWebsocketWriter(ws, datachan)
        go PingResponse(ws)
    })
}


func allUserWebsocketWriter(ws *websocket.Conn, datachan chan *[]storage.UserResponse) {
    conf := storage.GetConfig()
    pingticker := time.NewTicker(pingPeriod)
    defer func() {
        pingticker.Stop()
        ws.Close()
    }()

    userresponse, err := conf.Database.GetAllUsers()
    if err != nil {
        conf.Log.Errorf("Failed to query users from database: %v", err)
        return
    }

    ws.SetWriteDeadline(time.Now().Add(writeWait))
    err = ws.WriteJSON(&userresponse)
    if err != nil {
        conf.Log.Debugf("Failed to write initial user response: %v", err)
        return
    }

    for {
        select {
        case data := <-datachan:
            ws.SetWriteDeadline(time.Now().Add(writeWait))
            err := ws.WriteJSON(&data)
            if err != nil {
                conf.Log.Debugf("Failed to write data to Websocket: %v", err)
                return
            }
        case <-pingticker.C:
            ws.SetWriteDeadline(time.Now().Add(writeWait))
            conf.Log.Debugf("Sending Ping Message to Client")
            if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
                conf.Log.Debugf("Failed to write ping message to Websocket: %v", err)
                return
            }
        }
    }
}

Basically, we are posting the current data to the new Websocket Connection, when it is updated - this does always work. Afterwards, if the database changes, it posts the updated userlist into the channel - the websocket should then post it to the client which updates the list. We are also sending ping messages - which fail (as seen in the log above). The client itself does not log any errors or closes the websocket.

  • 写回答
  • 好问题 提建议
  • 追加酬金
  • 关注问题
  • 邀请回答

1条回答 默认 最新

相关推荐 更多相似问题