websocket诞生背景
当我们的客户端需要和服务器交互数据时,我们通常用轮询来请求数据,客户端以一定的时间间隔向服务端发出请求,以频繁请求的方式来保持客户端和服务器端的同步。这样频繁的请求中,服务端数据很可能没有更新,从而会带来很多无谓的网络传输,所以这是一种非常低效的实时方案。
而通过websocket,浏览器通过JavaScript向服务器发出建立WebSocket连接的请求,连接建立以后,客户端和服务器端就可以通过TCP连接直接交换数据,当服务器数据更新后,向客户端推送数据,避免了不必要的网络传输浪费。
|
|
如果希望websocket连接一直保持,我们会在close或者error上绑定重新连接方法。这样一般正常情况下失去连接时,触发onclose方法,我们就能执行重连了。
心跳重连缘由
在使用websocket过程中,可能会出现网络断开的情况,比如信号不好,或者网络临时性关闭,这时候websocket的连接已经断开,
而浏览器不会执行websocket 的 onclose方法,我们无法知道是否断开连接,也就无法进行重连操作。
如果当前发送websocket数据到后端,一旦请求超时,onclose便会执行,这时候便可进行绑定好的重连操作。
因此websocket心跳重连就应运而生。
心跳重连简单实现
|
|
当onopen也就是连接上时,我们便开始start计时,如果在定时时间范围内,onmessage获取到了服务器的消息,我们就重置倒计时,
所以在距离上次从服务器获取到消息,闲置60秒之后我们才会心跳检测,这个检测时间可以自己根据自身情况设定。
当心跳检测send执行之后,如果当前websocket是断开状态,发送超时之后,onclose方法便会被执行,重连也执行了。
如此一来,我们判断前端主动断开ws的心跳检测就实现了。为什么说是前端主动断开,因为当前这种情况主要是通过前端ws的事件来判断的,后面说后端主动断开的情况。
解决多端浏览器访问问题
多端浏览器发送的ws链接不同,以此区分,但一个浏览器的多个tab页面,ws链接一样,这样会出现一个服务器对应多个客户端的情况,下面介绍解决方案。
解决多端浏览器访问问题
多个tab之间使用领导者思想,始终只保留一个leader作为主页面,即websocket链接的发起者,具体实现思路如下:
1.每个页面加载时,都保存当前时间作为当前页面的页面ID
2.设置一个1s的定时器,定时检测localstorage
3.若发现localstorage中不存在leaderID,则表示当前页面不存在leader,则把自己的ID,以及当前时间分别存在localstorage的leaderID和leaderTime中
4.页面设置leader后,定时器定时检查当前leaderID是否与自己的id一致,如果一致,则修改leaderTime为当前时间,表示leader页面尚未关闭
5.若leaderTime与当前时间间隔超过3s,则表示leader页面已死亡,需要有一个新leader诞生
6.谁最先发现了前任leader已死,谁就自然成为新任leader,将自己的ID存为leaderID,且更新当前时间为leaderTime,表示当前时间存在leader
|
|
数据显示
ws建立成功后,接下来就是显示数据的部分,由于情况较多,采用图表展示:
| 数字: | |
|---|---|
| leader 新页面 | 接收数据时 dispatch(setCount()), 使用定时器this.props.dispatch(fetchCount())显示数据 |
| 子页面 新页面 | 接收数据时 localStorage.setItem(‘warningCount’, ${obj.req_args.apns.aps.alert}-${Date.now()}); 使用window.addEventListener(‘storage’, e => { 触发dispatch显示数据 之所以加上时间戳是因为当localStorage连续保存相同数据时,页面不再触发storage事件 |
| leader 旧页面 | 接收数据时 window.warningCount = obj.req_args.apns.aps.alert; 在旧页面使用this.setState({num: window.warningCount})显示数据 |
| 子页面 旧页面 | 同新页面的子页面,但要在旧页面代码中写storage函数,因为要使用setstate显示数据 |
| 文字: | |
|---|---|
| leader 新页面 | 接收数据时 notification.warning({ 并显示数据 |
| 子页面 新页面 | 接收数据时 localStorage.setItem(‘warningMessage’, ${obj.req_args.apns.aps.alert}-${Date.now()}); 使用window.addEventListener(‘storage’, e => { 触发notification显示数据 |
| leader 旧页面 | 同 leader 新页面 |
| 子页面 旧页面 | 同 子页面 新页面 |
注:保存localStorage的主页面不会触发storage事件
代码示例
|
|