需求:服务端出现数据改动之后,web端提示用户某某数据改动(本文主要涉及web端的处理)

想法:

  1. 通过web端轮询http请求,这种逻辑简单易懂,前后端的开发也比较简单,但是一直循环发送http请求不适合用户较多,注重性能的web应用。(http请求会带上大量头部信息,实际上有效数据可能较少,浪费带宽)
  2. 通过html5的websocket协议,而不是通过http请求来实现,这种情况下连接一旦建立,就可以双方互相发送数据,从性能方面来讲比轮询要优越,当然开发上会相对复杂一点。这里采用websocket的方法来实现。

WebSocket简介

WebSocket是基于TCP连接的协议,方便客户端和服务端建立端对端通信。
客户端向服务器发送WebSocket连接请求,连接建立之后,双方就可以通过TCP来建立连接交换数据
后台提供的WebSocket连接地址一般是ws://www.test.com/project/interface
前面是ws开头而不是http开头,后台接口也是要单独开发的。

如果有wss开头的,其中 wss 表示在 TLS 之上的 Websocket

参考:https://www.runoob.com/html/html5-websocket.html
常用主要注意WebSocket的4个事件和2个方法

WebSocket 协议本质上是一个基于 TCP 的协议。
为了建立一个 WebSocket 连接,客户端浏览器首先要向服务器发起一个 HTTP 请求,这个请求和通常的 HTTP 请求不同,包含了一些附加头信息,其中附加头信息"Upgrade: WebSocket"表明这是一个申请协议升级的 HTTP 请求,服务器端解析这些附加的头信息然后产生应答信息返回给客户端,客户端和服务器端的 WebSocket 连接就建立起来了

兼容性暂时没有测试过,因为目前产品都会要客户使用chrome-,-

websocket和socket是不同的东西,具体区别这里不做展开了

额外注意点

如果浏览器刷新页面,websocket的连接是会断开的!

服务器的nginx一般会进行配置,websocket如果一小段时间不和服务器通信是会被断开的!

实际开发代码

这里项目是Vue+Element的前端项目
使用WebSocket不用引入其他包

创建WebSocket工具文件

先建立utilWebSocket.js工具文件,用来存放WebSocket相关的代码,然后将websocket在main.js中注册到全局方便调用
这里先定义一个websocket变量,代表websocket连接

//websocket对象
let webSocket = null;

创建WebSocket连接

websocket连接只需要通过 webSocket = new WebSocket(wsUri); 来创建就可以了,wsUri是后端提供的ws接口地址
这里通过try catch包裹创建websocket的代码,来保证如果第一时间没有建立连接,之后也会继续去申请建立连接

//创建webSocket连接
let createWebSocket = () => {
    try {
        let path = window.location.href;
        const wsUri = path.substring(0, path.indexOf("#")).replace('http', 'ws') + `customer/websocket/${sessionStorage.getItem('addUserId')}`;
        //其实只需要定义一个变量存放后端提供的WebSocket接口地址,然后
        webSocket = new WebSocket(wsUri);
        //初始化websocket连接
        initWebsocket();
    } catch (e) {
        console.log('尝试创建连接失败');
        //如果无法连接上webSocket 那么重新连接!可能会因为服务器重新部署,或者短暂断网等导致无法创建连接
        reConnect();
    }
};

reConnect是一个重连方法,这样如果第一时间没有建立连接或者之后由于各种原因连接断开了,就可以调用reConnect方法

//连接标识 避免重复连接
let isConnect = false;
//断线重连后,延迟5秒重新创建WebSocket连接  rec用来存储延迟请求的代码
let rec;
//定义重连函数
let reConnect = () => {
    console.log('尝试重新连接');
    //如果已经连上就不在重连了
    if(isConnect) return;
    rec&&clearTimeout(rec);
    // 延迟5秒重连  避免过多次过频繁请求重连
    rec=setTimeout(function(){
        createWebSocket();
    },5000);
};

初始化WebSocket

//初始化webSocket连接
let initWebsocket = () => {
//WebSocket连接建立之后会调用onopen方法
    webSocket.onopen = function (e) {
        console.log('open');
        //连接建立后修改标识
        isConnect=true;
        // 建立连接后开始心跳
        // 因为nginx一般会设置例如60s没有传输数据就断开连接  所以要定时发送数据
        heartCheck.start();
    };    
//当websocket收到服务器发送的信息之后  会调用onmessage方法  getMsg用来封装获取到服务器的消息进行处理,下面会说明
    webSocket.onmessage = function (e) {
        getMsg(e);
        //获取消息后 重置心跳
        heartCheck.reset();
    };
//当websocket因为各种原因(正常或者异常)关闭之后,会调用onclose方法
    webSocket.onclose = function (e) {
        console.log('close');
        //连接断开后修改标识
        isConnect=false;
    };
//当websocket因为异常原因(比如服务器部署、断网等)关闭之后,会调用onerror方法
//在onerror中需要调用reConnect方法重连服务器
    webSocket.onerror = function (e) {
        console.log('error');
        //连接断开后修改标识
        isConnect=false;
        //连接错误 需要重连
        reConnect();
    };
};

这里用到了heartCheck的两个方法,
heartCheck是为了应对nginx关闭不传数据的连接,
每一段时间传一个特殊数据,类似发送心跳包

心跳防止断开连接

heartCheck心跳工具的代码如下

//心跳发送/返回的信息
//服务器和客户端收到的信息内容如果如下 就识别为心跳信息 不要做业务处理
let checkMsg='heartbeat';

//心跳设置
var heartCheck = {
    //每段时间发送一次心跳包 这里设置为20s
    timeout: 20000,
    //延时发送消息对象(启动心跳新建这个对象,收到消息后重置对象)
    timeoutObj: null,
    //一段时间后发送心跳包
    start: function () {
        this.timeoutObj = setTimeout(function () {
            if(isConnect) webSocket.send(checkMsg);
        }, this.timeout);
    },
//    接收到服务器的信息之后要重置心跳发送的方法
    reset: function () {
        clearTimeout(this.timeoutObj);
        this.start();
    },
};

核心思想就是只要客户端收到消息了,就结束这一轮的心跳,开始下一轮的心跳!

收到信息后区分业务信息和心跳信息

//获得消息之后   区别是心跳还是业务信息  如果是业务信息特殊处理(这里就用Element的notify才处理提醒)
let getMsg = (e) => {
    console.log('message');
    console.log(e.data);
    if ((e.data != '连接成功') && (e.data != checkMsg)) {
        this.$notify.error({
            title: '失败',
            message: e.data,
            duration: 0,
        });
    }
};

定义关闭连接的方法,方便登出时使用

这里将启动和关闭连接的方法暴露出去,登入和登出时来调用这两个方法

//关闭连接
let closeWebSocket = () => {
    webSocket.close();
};


export default {
    createWebSocket: createWebSocket,
    closeWebSocket: closeWebSocket,
}

将关闭和创建连接的方法在main中注册全局,方便业务代码中使用

import utilWebsocket from './util/utilWebSocket'
Vue.prototype.closeWebSocket = utilWebsocket .closeWebSocket;
Vue.prototype.createWebSocket = utilWebsocket .createWebSocket;

这样直接在登录之后调用this.createWebSocket()方法就可以了
登出调用this.closeWebSocket()方法就可以了

chrome调试

在network中开启WS过滤,就可以看到当前项目的websocket连接情况了
注意Header中的Connection属性是Upgrade
Upgrade属性是websocket

[外链图片转存失败(img-hom2JhzK-1562862891215)(./1562857587799.png)]

webpack和nginx中的代理设置

websocket的代理设置和http是不一样的

webpack配置websocket代理

普通的http请求的代理

'/admin/': {
                target: 'http://192.168.166.107:8081',
                changeOrigin: true,
            },

websocket的代理如下

'/customer/websocket/': {
                target: 'ws://192.168.166.107:8083',
                ws:true,
                secure:false,
            },

nginx配置websocket代理

服务器配置这个代理就比webpack要复杂一些,较高版本的nginx一般都支持webpack代理
自己用的版本是否支持自行查询。。
以下是简单的配置 还有其他详细配置这里就不展开了

location /customer/websocket {
            proxy_pass http://192.168.166.112:8083/customer/websocket;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }

nginx的配置一定要注意!不然会被误认为是http的get请求 一直返回200状态码

Logo

前往低代码交流专区

更多推荐