位置:首页 > web前端 > javascript

如何解决webSocket自动断开连接

dearweb 发布:2022-07-01 23:51:20阅读:

一般情况下,前端页面连接WebSocket服务的时候都是通过Nginx等负载均衡,然后由Nginx去代理连接后端的socket服务。Nginx的配置类似如下:

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}
location / {
    proxy_pass https://socket;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
         
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
}


如果建立连接之后不做一些措施,那么可能会有各种各样的原因会导致socket断开,最好在socket断开时将错误打印出来

ws.onclose = function (ev) {
  console.log('socket 断开: ' + ev.code + ' ' + ev.reason + ' ' + ev.wasClean)
}

socket断开时,会触发CloseEvent, CloseEvent会在连接关闭时发送给使用 WebSocket 的客户端,它在 WebSocket 对象的 onclose 事件监听器中使用。 CloseEvent有三个字段需要注意, 通过分析这三个字段,一般就可以找到断开原因:


CloseEvent.code: code是错误码,是整数类型


CloseEvent.reason: reason是断开原因,是字符串


CloseEvent.wasClean: wasClean表示是否正常断开,是布尔值。一般异常断开时,该值为false


image.png


为了保证socket稳定,不断开,最好也是最简单的办法是添加一些逻辑,一直保持socket处在连接的状态。常见的做法就是间隔发ping消息给服务端,服务端接收到这个消息之后返回pong消息,以此来保持心跳,以防sock断开。我们常见的ping消息和pong消息实际上是发送了一个文本消息,这个消息的内容是ping或者pong,甚至是heatbeat等等,但是从socket协议来说是有设计ping消息和pong消息的。在socket的数据帧中,有一个opcode,它表明了socket的数据帧是什么类型的:


%x0:表示一个延续帧。当Opcode为0时,表示本次数据传输采用了数据分片,当前收到的数据帧为其中一个数据分片。


%x1:表示这是一个文本帧(frame)


%x2:表示这是一个二进制帧(frame)


%x3-7:保留的操作代码,用于后续定义的非控制帧。


%x8:表示连接断开。


%x9:表示这是一个ping操作。


%xA:表示这是一个pong操作。


%xB-F:保留的操作代码,用于后续定义的控制帧。


规范的心跳应该是在opcode里定义type:ping(9)才对,消息的内容是null,什么都没有,这才是最轻量级最规范的websocket心跳机制。一般情况下,使用发文本消息的方式也是没啥问题的,无非就是多消耗了一点流量和带宽,调试起来也容易一些,有可能心跳消息本身就会带一些业务数据。


js代码如下:


var lockReconnect = false;  

var ws = null;          

var wsUrl = 'wss://127.0.0.1/socket'

createWebSocket(wsUrl);  



function createWebSocket(url) {

    try{

        if('WebSocket' in window){

            ws = new WebSocket(url);

        }

        initEventHandle();

    }catch(e){

        reconnect(url);

        console.log(e);

    }    

}



function initEventHandle() {

    ws.onclose = function (ev) {

        reconnect(wsUrl);

         console.log('socket 断开: ' + ev.code + ' ' + ev.reason + ' ' + ev.wasClean)

    };

    ws.onerror = function (ev) {

        reconnect(wsUrl);

        console.log("llws连接错误!");

    };

    ws.onopen = function () {

        heartCheck.reset().start();      

        console.log("llws连接成功!"+new Date().toLocaleString());

    };

    ws.onmessage = function (message) {    

        heartCheck.reset().start();      //拿到任何消息都说明当前连接是正常的

        console.log("llws收到消息啦:" +message.data);

        if(message.data!='pong'){

            var msg = JSON.parse(message.data);

        }

    };

}


当窗口关闭时,主动去关闭websocket连接

window.onbeforeunload = function() {

    ws.close();

}  



function reconnect(url) {

    if(lockReconnect) return;

    lockReconnect = true;

    setTimeout(function () {     //没连接上会一直重连,设置延迟避免请求过多

        createWebSocket(url);

        lockReconnect = false;

    }, 2000);

}

 

var heartCheck = {

    timeout: 3000,      

    timeoutObj: null,

    serverTimeoutObj: null,

    reset: function(){

        clearTimeout(this.timeoutObj);

        clearTimeout(this.serverTimeoutObj);

        return this;

    },

    start: function(){

        var self = this;

        this.timeoutObj = setTimeout(function(){

            ws.send("ping");

            console.log("ping!")

            self.serverTimeoutObj = setTimeout(function(){

              //如果超过一定时间还没重置,说明后端主动断开了

                ws.close();      

            }, self.timeout)

        }, this.timeout)

    }

}


服务端Java代码:

@OnMessage  

public void onMessage(String message, Session session) {  

        if(message.equals("ping")){


        }else{

               
        }

}


24人点赞 返回栏目 提问 分享一波

小礼物走一波,支持作者

还没有人赞赏,支持一波吧

留言 评论仅代表网友个人 留言列表

暂无留言,快来抢沙发吧!

手机扫码查看 手机扫码查看