小洋侃剧 發表於 2021-9-10 21:55:00

uni-app中websocket的使用 断开重连、心跳机制

<h1 id="前言">前言</h1>
<p>最近关于H5和APP的开发中使用到了webSocket,由于web/app有时候会出现网络不稳定或者服务端主动断开,这时候导致消息推送不了的情况,需要客户端进行重连。查阅资料后发现了一个心跳机制,也就是客户端间隔一段时间就向服务器发送一条消息,如果服务器收到消息就回复一条信息过来,如果一定时间内没有回复,则表示已经与服务器断开连接了,这个时候就需要进行重连。</p>
<p>被动断开则进行重连,主动断开的不重连。</p>
<p>说明:下图针对两个Tab项(Open Trades 和 Closed Trades),只希望在 tabIndex = 0 (Open Trades 高亮时)触发webSocket , 如果点击第二个栏目 , tabIndex = 1(Closed Trades高亮时)则主动关闭webSodket连接。</p>
<p>TabIndex = 0 时 ,被动断开则自动重连</p>
<p>原文链接:uni-app中websocket的使用 断开重连、心跳机制</p>
<h1 id="效果">效果</h1>
<ol>
<li>webScoket连接并接收推送的消息<br>
<img src="https://bu.dusays.com/2021/09/10/214077a6000a1.jpg"></li>
<li>将接收的消息转换成目标数据,并渲染<br>
<img src="https://bu.dusays.com/2021/09/10/5678988bb155e.jpg"></li>
<li>如果主动关闭,则不进行重连,监听关闭事件<br>
<img src="https://bu.dusays.com/2021/09/10/97f43863d23b6.jpg"></li>
<li>显示已关闭,不重连<br>
<img src="https://bu.dusays.com/2021/09/10/6eee925c9f2dd.jpg"></li>
<li>监听错误事件,比如地址,协议错误等,则会自动重连五次,五次重连仍失败后则需要进行手动重连<br>
<img src="https://bu.dusays.com/2021/09/10/0a8fd12f2a4c4.jpg"><br>
<img src="https://bu.dusays.com/2021/09/10/d817f0ed5d7fb.jpg"><br>
<img src="https://bu.dusays.com/2021/09/10/8b6f83a0c771f.jpg"></li>
<li>如果服务端主动断开,心跳机制会每隔一段时间发送一条数据给服务端,如果没有回复则会进行webScoket重连。<br>
<img src="https://bu.dusays.com/2021/09/10/ced1a2fddcf20.jpg"><br>
<img src="https://bu.dusays.com/2021/09/10/4c0191471d75a.jpg"></li>
</ol>
<h1 id="代码">代码</h1>
<ol>
<li>新建 socket.js , 将以下代码复制进去 ,向外暴露。</li>
</ol>
<pre><code class="language-JS">import api from '@/common/js/config.js' // 接口Api,图片地址等等配置,可根据自身情况引入,也可以直接在下面url填入你的 webSocket连接地址
class socketIO {
        constructor(data, time, url) {
      this.socketTask = null
                this.is_open_socket = false //避免重复连接
                this.url = url ? url : api.websocketUrl//连接地址
                this.data = data ? data : null
                this.connectNum = 1 // 重连次数
                this.traderDetailIndex = 100 // traderDetailIndex ==2 重连
                this.accountStateIndex = 100 // traderDetailIndex ==1 重连
                this.followFlake = false // traderDetailIndex == true 重连
                //心跳检测
                this.timeout = time ? time : 15000 //多少秒执行检测
                this.heartbeatInterval = null //检测服务器端是否还活着
                this.reconnectTimeOut = null //重连之后多久再次重连
        }

        // 进入这个页面的时候创建websocket连接【整个页面随时使用】
        connectSocketInit(data) {
                this.data = data
                this.socketTask = uni.connectSocket({
                        url: this.url,
                        success: () =&gt; {
                                console.log("正准备建立websocket中...");
                                // 返回实例
                                return this.socketTask
                        },
                });
                this.socketTask.onOpen((res) =&gt; {
                        this.connectNum = 1
                        console.log("WebSocket连接正常!");
                        this.send(data)
                        clearInterval(this.reconnectTimeOut)
                        clearInterval(this.heartbeatInterval)
                        this.is_open_socket = true;
                        this.start();
                        // 注:只有连接正常打开中 ,才能正常收到消息
                        this.socketTask.onMessage((e) =&gt; {
                                // 字符串转json
                                let res = JSON.parse(e.data);
                                console.log("res----------&gt;", res) // 这里 查看 推送过来的消息
                                if (res.data) {
                                        uni.$emit('getPositonsOrder', res);
                                }
                        });
                })
                // 监听连接失败,这里代码我注释掉的原因是因为如果服务器关闭后,和下面的onclose方法一起发起重连操作,这样会导致重复连接
                uni.onSocketError((res) =&gt; {
                        console.log('WebSocket连接打开失败,请检查!');
                        this.socketTask = null
                        this.is_open_socket = false;
                        clearInterval(this.heartbeatInterval)
                        clearInterval(this.reconnectTimeOut)
                        uni.$off('getPositonsOrder')
                        if (this.connectNum &lt; 6) {
                                uni.showToast({
                                        title: `WebSocket连接失败,正尝试第${this.connectNum}次连接`,
                                        icon: "none"
                                })
                                this.reconnect();
                                this.connectNum += 1
                        } else {
                                uni.$emit('connectError');
                                this.connectNum = 1
                        }

                });
                // 这里仅是事件监听【如果socket关闭了会执行】
                this.socketTask.onClose(() =&gt; {
                        console.log("已经被关闭了-------")
                        clearInterval(this.heartbeatInterval)
                        clearInterval(this.reconnectTimeOut)
                        this.is_open_socket = false;
                        this.socketTask = null
                        uni.$off('getPositonsOrder')
                        if (this.connectNum &lt; 6) {
                                this.reconnect();
                        } else {
                                uni.$emit('connectError');
                                this.connectNum = 1
                        }

                })
        }
    // 主动关闭socket连接
        Close() {
                if (!this.is_open_socket) {
                        return
                }
                this.socketTask.close({
                        success() {
                                uni.showToast({
                                        title: 'SocketTask 关闭成功',
                                        icon: "none"
                                });
                        }
                });
        }
        //发送消息
        send(data) {
                console.log("data----------&gt;", data);
                // 注:只有连接正常打开中 ,才能正常成功发送消息
                if (this.socketTask) {
                        this.socketTask.send({
                                data: JSON.stringify(data),
                                async success() {
                                        console.log("消息发送成功");
                                },
                        });
                }
        }
        //开启心跳检测
        start() {
                this.heartbeatInterval = setInterval(() =&gt; {
                        this.send({
                                "traderid": 10260,
                                "type": "Ping"
                        });
                }, this.timeout)
        }
        //重新连接
        reconnect() {
                //停止发送心跳
                clearInterval(this.heartbeatInterval)
                //如果不是人为关闭的话,进行重连
                if (!this.is_open_socket &amp;&amp; (this.traderDetailIndex == 2 || this.accountStateIndex == 0 || this
                        .followFlake)) {
                        this.reconnectTimeOut = setInterval(() =&gt; {
                                this.connectSocketInit(this.data);
                        }, 5000)
                }
        }
        /**
       * @description 将 scoket 数据进行过滤
       * @param {array} array
       * @param {string} type 区分 弹窗 openposition 分为跟随和我的
       */
        arrayFilter(array, type = 'normal', signalId = 0) {
                let arr1 = []
                let arr2 = []
                let obj = {
                        arr1: [],
                        arr2: []
                }
                arr1 = array.filter(v =&gt; v.flwsig == true)
                arr2 = array.filter(v =&gt; v.flwsig == false)
                if (type == 'normal') {
                        if (signalId) {
                                arr1 = array.filter(v =&gt; v.flwsig == true &amp;&amp; v.sigtraderid == signalId)
                                return arr1
                        } else {
                                return arr1.concat(arr2)
                        }
                } else {
                        if (signalId &gt; 0) {
                                arr1 = array.filter(v =&gt; v.flwsig == true &amp;&amp; v.sigtraderid == signalId)
                                obj.arr1 = arr1
                        } else {
                                obj.arr1 = arr1
                        }
                        obj.arr2 = arr2
                       
                        return obj
                }
        }
}
module.exports = socketIO
</code></pre>
<ol start="2">
<li>在入口文件中 将 socketIO 挂载在 Vue 原型上 , 也可以按需引入置顶页面 。</li>
</ol>
<pre><code class="language-JS">import socketIO from '@/common/js/scoket.js'
Vue.prototype.socketIo = new socketIO()
</code></pre>
<ol start="3">
<li>在需要用到webSocket的页面中使用如下方法(可根据自身业务需求进行整改)</li>
</ol>
<pre><code class="language-JS">scoketClose() {
    this.socketIo.connectNum = 1
    const data = {
      "value1": "demo1"
      "value2": "demo2"
    }
    this.socketIo.send(data) // 这是给后端发送特定数据 关闭推送
    this.socketIo.Close() // 主动 关闭连接 , 不会重连
},

getWebsocketData() {
    // 要发送的数据包
    const data = {
      "value": "value1",
      "type": "type1"
    }
    // 打开连接
    this.socketIo.connectSocketInit(data)
    // 接收数据
    uni.$on("getPositonsOrder", (res) =&gt; {
      this.connect = true
      const {
            Code,
            data
      } = res
      if (Code == xxxx) {
            // 根据后端传过来的数据进行 业务编写。。。
      } else {
         
      }
    })
    // 错误时做些什么
    uni.$on("connectError", () =&gt; {
      this.connect = false
      this.scoketError = true
    })
}
</code></pre>
<ol start="4">
<li>离开页面,记得断开连接。</li>
</ol>
<pre><code class="language-JS">onUnload() {
    this.scoketClose()
    this.socketIo.traderDetailIndex = 100 // 初始化 tabIndex
}
</code></pre>
<h1 id="遇到问题">遇到问题</h1>
<p>如果在使用中遇到什么问题,可以给我留言,看到留言后会在第一时间进行回复 。</p>


</div>
<div id="MySignature" role="contentinfo">
    <p>本文来自博客园,作者:啊睦,转载请注明原文链接:https://www.cnblogs.com/fe32/p/15253185.html</p><br><br>
来源:https://www.cnblogs.com/fe32/p/15253185.html
頁: [1]
查看完整版本: uni-app中websocket的使用 断开重连、心跳机制