猿问

关闭在服务器端发送但客户端仍然连接

我有一个通过 API 和 websocket 进行通信的应用程序。websocket 用于将更新的用户数据发布到客户端,如果它在数据库中发生更改 - 这非常有效,除了在某些情况下 websocket 不接收任何数据的情况。几秒钟后,websocket 再次开始工作。

服务器日志(首先,websocket 不工作并重新开始工作)

msg="无法将数据写入 Websocket: websocket: close sent"

msg="正在向客户端发送 Ping 消息"

msg="无法将 ping 消息写入 Websocket:websocket:关闭发送"

msg="正在向客户端发送 Ping 消息"

msg="正在向客户端发送 Ping 消息"

msg="正在向客户端发送 Ping 消息"

msg="正在向客户端发送 Ping 消息"

客户端代码:

<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>

基本上,我们将当前数据发布到新的 Websocket 连接,当它更新时——这总是有效的。之后,如果数据库发生变化,它会将更新后的用户列表发布到频道中——然后 websocket 应将其发布到更新列表的客户端。我们还发送 ping 消息 - 失败(如上面的日志所示)。客户端本身不会记录任何错误或关闭 websocket。



繁星coding
浏览 233回答 1
1回答

尚方宝剑之说

该websocket: close sent错误表明服务器向客户端发送了关闭消息。因为应用程序服务器代码不发送消息,所以连接必须发送消息以响应来自客户端的关闭消息。关闭消息作为错误从 websocket 读取方法返回。因为没有记录任何消息,所以客户端一定发送了“离开”关闭消息(唯一没有记录的错误)。当websocket连接返回错误时,读写goroutine关闭连接返回。连接未保持打开状态。读取和写入 goroutine 不会检测到另一个已关闭连接,直到从连接上的方法调用返回错误。读取 goroutine 会快速检测到关闭的连接,因为它始终在读取,但写入 goroutine 可能会有延迟。这可能是应用程序的问题要让写入 goroutine 快速退出,请使用通道向写入 goroutine 发出信号。有可能dataChan可用于此目的,但我不确定,因为该问题不包含有关如何管理频道的信息。假设通道可以使用,读取 goroutine 应该关闭dataChan。writer 应该检测到关闭的通道并退出 goroutine:...for {    select {    case data, ok := <-datachan:        if !ok {           // Done writing, return           return        }        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        }        ...这是Gorilla Chat Example使用的方法。如果dataChan不能使用,为此引入一个新的通道。在处理程序中创建通道并将通道传递给读写 goroutines: done := make(chan struct{}) go allUserWebsocketWriter(ws, stop, datachan) go PingResponse(ws, stop)从读取 goroutine 返回时关闭通道:func PingResponse(ws *websocket.Conn, done chan struct{}) {    defer close(done)    conf := storage.GetConfig()    ...在写gorountine的通道上选择:...for {    select {    case <-done:        return    case data := <-datachan:        ws.SetWriteDeadline(time.Now().Add(writeWait))        err := ws.WriteJSON(&data)        ...这导致写 goroutine 在读 goroutine 退出后快速退出。这两种方法都降低了在连接上写入返回错误的可能性websocket: close sent,但它们并没有消除这种可能性。该错误是预期的,因为读取 goroutine 可以在写入 goroutine 写入消息之前关闭连接。无论如何,证据是客户端正在关闭连接。未关闭的连接不是问题所在。
随时随地看视频慕课网APP

相关分类

Go
我要回答