Web前端

vue使用SockJS实现webSocket通信

2021-10-26  本文已影响0人  yichen_china

以前使用websocket都是使用

window.webSocket = new WebSocket('ws://' + config.webSocketUrl + '/webData/websocket?token=' + token + '&username=' + username);

这种方式进行操作。由于项目要求,需要访问网关因此需要使用http的连接方式进行socket信息推送,以下用的是 SockJS。

2020-02-19更新

在项目前期开发,我们都是在全局的js文件中定义socket的连接ip和端口,在页面调用。但在项目优化过程中,我们希望可以直接使用webpack的代理模式,直接通过代码进行请求,这样页面就不需要进行socket地址的配置。
优点:(1)页面减少全量变量的配置;(2)系统方法调用的统一性,和其他普通的接口调用代理一致。
缺点:(1)由于在页面中配置的代理,而不是直接请求http:xxxx,导致需要在代理服务器中(例如ngnix)上多添加一个代理配置。相当于把以前http的直接请求方式变成代理转发请求。
具体更改模式为:
  1、在自定义的websocket.js文件中,创建SockJS对象:
新写法:const socket = new SockJS('/bullet');// 连接SockJS的endpoint名称为"bullet"
    旧写法:let socket = new SockJS('http://'+config.webSocketUrl+'/bullet');//连接SockJS的endpoint名称为"bullet"
  2、在项目根目录下的config/index.js文件中(vue-cli2.0),或者vue.config.js(vue-cli3.0)添加代理配置即可。

proxyTable: {
  '/bullet': {
    target: target,  //target为目标变量
    ws: true,
    pathRewrite: {
      '^/bullet': '/bullet'
    },
  }
},

****2019-04-11更新** 随着项目的模块化,需要把websocket相关的功能独自创建一个模块进行引入使用,以下是本人的操作方式:

1.在utils目录下创建一个js文件,可以命名为:websocket.js*</pre>

image

2.在websocket.js文件中写入相关的socket.io代码

说明:config.webSocketUrl是一个url地址的变量。主要是在vue项目中的static文件夹下创建一个js文件(不会被webpack压缩),定义全局常量、变量,并在index.html中做为一个原生的js文件使用<script>标签引入即可。在项目部署过程中,直接修改js文件,即可对相应的数据进行

// socket功能
import SockJS from "sockjs-client";
import Stomp from "stompjs";
import store from "../store";

export function connectionSocket() {
  let socket = new SockJS('http://'+config.webSocketUrl+'/bullet');//连接SockJS的endpoint名称为"bullet"
  console.log('socket连接地址:'+'http://'+config.webSocketUrl+'/bullet');
  // 获取STOMP子协议的客户端对象
  let stompClient = Stomp.over(socket);
  // 定义客户端的认证信息,按需求配置
  let headers = {
    Authorization:store.getters.token
  };

  // 拦截输出的一大堆垃圾信息
  stompClient.debug = function (str) {
    $("#debug").append(str + "\n");
  };
  // 向服务器发起websocket连接
  stompClient.connect(headers,() => {
    stompClient.subscribe('/topic/getResponse', (response) => { // 订阅服务端提供的某个topic
      if (response.body) {
        const repObj = JSON.parse(response.body);
        if (repObj.data.webSocketType == 'ISEVehicle') { //监控管理,新版车辆监控
          if (repObj.status == 200) {
            store.dispatch('carMonitorFun', repObj);
          }  else if (repObj.data.webSocketType == 'vehicleAlarm') { //首页,车辆告警数据推送
          if (repObj.status == 200) {
            store.commit('vehicleAlarmMUTA', repObj.data);
          }
        }
      }
    });
    stompClient.subscribe('/user/'+store.getters.userRegionCode+'/queue/getResponse', (response) => { // 订阅服务端提供的某个topic
      if (response.body) {
        const repObj = JSON.parse(response.body);
         if (repObj.data.webSocketType == 'personAlarm') { //首页,人脸预警数据推送
          if (repObj.status == 200) {
            store.commit('personAlarmMUTA', repObj.data);
          }
        }
        else if (repObj.data.webSocketType == 'vehicleAlarm') { //首页,车辆告警数据推送
          if (repObj.status == 200) {
            store.commit('vehicleAlarmMUTA', repObj.data);
          }
        }
      }
    });
    stompClient.subscribe('/user/'+store.getters.token+'/queue/getResponse', (response) => { // 订阅服务端提供的某个topic
      if (response.body) {
        let repObj = JSON.parse(response.body);
        if (repObj.data.webSocketType =='task') { store.commit('monitorStatus', repObj);} //任务列表//当监控到websocket有数据返回的时候,修改monitorStatus使其发生变化即可
        else if (repObj.data.webSocketType == 'networkConfig') { store.commit('monitorStatusMUTA', repObj);}//联网配置
       
      }
    });
  }, (err) => {
    // 连接发生错误时的处理函数
    console.log('失败')
  });
}

3.在页面需要初始化的地方因为该js文件即可

image

# 2018-12-13创建

先安装 sockjs-client 和 stompjs

npm install sockjs-client

npm install stompjs
import SockJS from  'sockjs-client';
import  Stomp from 'stompjs';
export default {
    data(){
        return {
            stompClient:'',
            timer:'',
        }
    },
    methods:{
        initWebSocket() {
            this.connection();
            let that= this;
            // 断开重连机制,尝试发送消息,捕获异常发生时重连
            this.timer = setInterval(() => {
                try {
                    that.stompClient.send("test");
                } catch (err) {
                    console.log("断线了: " + err);
                    that.connection();
                }
            }, 5000);
        },  
        connection() {
            // 建立连接对象
            let socket = new SockJS('http://10.10.91.4:8081/ws');
            // 获取STOMP子协议的客户端对象
            this.stompClient = Stomp.over(socket);
            // 定义客户端的认证信息,按需求配置
            let headers = {
                Authorization:''
            }
            // 向服务器发起websocket连接
            this.stompClient.connect(headers,() => {
                this.stompClient.subscribe('/topic/public', (msg) => { // 订阅服务端提供的某个topic
                    console.log('广播成功')
                    console.log(msg);  // msg.body存放的是服务端发送给我们的信息
                },headers);
                this.stompClient.send("/app/chat.addUser",
                    headers,
                    JSON.stringify({sender: '',chatType: 'JOIN'}),
                )   //用户加入接口
            }, (err) => {
                // 连接发生错误时的处理函数
                console.log('失败')
                console.log(err);
            });
        },    //连接 后台
        disconnect() {
            if (this.stompClient) {
                this.stompClient.disconnect();
            }
        },  // 断开连接
    },
    mounted(){
        this.initWebSocket();
    },
    beforeDestroy: function () {
        // 页面离开时断开连接,清除定时器
        this.disconnect();
        clearInterval(this.timer);
    }
}

问题
安装 sockjs-client、stompjs;在这儿要注意一下,我在"stompjs": "^2.3.3"这个版本发现,引入stompjs会报一个net模块找不到,需要在stompjs模块根目录下执行npm install net,这个是个奇葩的问题

进入到module目录下的stompjs目录,执行npm install net</pre>

image image

STOMP 客户端 API 整理

如何理解 STOMP 与 WebSocket 的关系:

  1. HTTP协议解决了 web 浏览器发起请求以及 web 服务器响应请求的细节,假设 HTTP 协议 并不存在,只能使用 TCP 套接字来 编写 web 应用,你可能认为这是一件疯狂的事情;
  2. 直接使用 WebSocket(SockJS) 就很类似于 使用 TCP 套接字来编写 web 应用,因为没有高层协议,就需要我们定义应用间所发送消息的语义,还需要确保连接的两端都能遵循这些语义;
  3. 同 HTTP 在 TCP 套接字上添加请求-响应模型层一样,STOMP 在 WebSocket 之上提供了一个基于帧的线路格式层,用来定义消息语义;

STOMP帧
STOMP帧由命令,一个或多个头信息、一个空行及负载(文本或字节)所组成;

其中可用的COMMAND 包括:

CONNECT、SEND、SUBSCRIBE、UNSUBSCRIBE、BEGIN、COMMIT、ABORT、ACK、NACK、DISCONNECT;

例:
发送消息

SEND 
destination:/queue/trade 
content-type:application/json 
content-length:44 
{“action”:”BUY”,”ticker”:”MMM”,”shares”,44}^@

订阅消息

SUBSCRIBE 
id:sub-1 
destination:/topic/price.stock.* 
^@

服务器进行广播消息

MESSAGE 
message-id:nxahklf6-1 
subscription:sub-1 
destination:/topic/price.stock.MMM 
{“ticker”:”MMM”,”price”:129.45}^@

客户端 API
引入stomp.js

<script type="application/javascript" src="http://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js"></script>

发起连接
客户端可以通过使用Stomp.js和sockjs-client连接

// 建立连接对象(还未发起连接)
var socket=new SockJS("/spring-websocket-portfolio/portfolio");

// 获取 STOMP 子协议的客户端对象
var stompClient = Stomp.over(socket);

// 向服务器发起websocket连接并发送CONNECT帧
stompClient.connect(
    {},
function connectCallback (frame) {
    // 连接成功时(服务器响应 CONNECTED 帧)的回调方法
        document.getElementById("state-info").innerHTML = "连接成功";
        console.log('已连接【' + frame + '】');
        stompClient.subscribe('/topic/getResponse', function (response) {
            showResponse(response.body);
        });
    },
function errorCallBack (error) {
    // 连接失败时(服务器响应 ERROR 帧)的回调方法
        document.getElementById("state-info").innerHTML = "连接失败";
        console.log('连接失败【' + error + '】');
    }
);

说明:

  1. socket连接对象也可通过WebSocket(不通过SockJS)连接
var socket=new WebSocket("/spring-websocket-portfolio/portfolio");
  1. stompClient.connect()方法签名:
client.connect(headers, connectCallback, errorCallback);

其中
headers表示客户端的认证信息,如:

var headers = {
  login: 'mylogin',
  passcode: 'mypasscode',
  // additional header
  'client-id': 'my-client-id'
};

若无需认证,直接使用空对象 “{}” 即可;

connectCallback 表示连接成功时(服务器响应 CONNECTED 帧)的回调方法;
errorCallback 表示连接失败时(服务器响应 ERROR 帧)的回调方法,非必须;

断开连接
若要从客户端主动断开连接,可调用 disconnect() 方法

client.disconnect(function () {
   alert("See you next time!");
};

该方法为异步进行,因此包含了回调参数,操作完成时自动回调;

心跳机制
若使用STOMP 1.1 版本,默认开启了心跳检测机制,可通过client对象的heartbeat field进行配置(默认值都是10000 ms):

client.heartbeat.outgoing = 20000;  // client will send heartbeats every 20000ms
client.heartbeat.incoming = 0;      // client does not want to receive heartbeats from the server
// The heart-beating is using window.setInterval() to regularly send heart-beats and/or check server heart-beats

发送信息
连接成功后,客户端可使用 send() 方法向服务器发送信息:

client.send(destination url[, headers[, body]]);

其中
destination url 为服务器 controller中 @MessageMapping 中匹配的URL,字符串,必须参数;
headers 为发送信息的header,JavaScript 对象,可选参数;
body 为发送信息的 body,字符串,可选参数;

例:

client.send("/queue/test", {priority: 9}, "Hello, STOMP");
client.send("/queue/test", {}, "Hello, STOMP");

订阅、接收信息
STOMP 客户端要想接收来自服务器推送的消息,必须先订阅相应的URL,即发送一个 SUBSCRIBE 帧,然后才能不断接收来自服务器的推送消息;
订阅和接收消息通过 subscribe() 方法实现:

subscribe(destination url, callback[, headers])

其中
destination url 为服务器 @SendTo 匹配的 URL,字符串;
callback 为每次收到服务器推送的消息时的回调方法,该方法包含参数 message;
headers 为附加的headers,JavaScript 对象;什么作用?
该方法返回一个包含了id属性的 JavaScript 对象,可作为 unsubscribe() 方法的参数;

例:

var headers = {ack: 'client', 'selector': "location = 'Europe'"};
var  callback = function(message) {
  if (message.body) {
    alert("got message with body " + message.body)
  } else {
    alert("got empty message");
  }
});
var subscription = client.subscribe("/queue/test", callback, headers);

取消订阅

var subscription = client.subscribe(...);

subscription.unsubscribe();

JSON 支持
STOMP 帧的 body 必须是 string 类型,若希望接收/发送 json 对象,可通过 JSON.stringify() and JSON.parse() 实现;
例:

var quote = {symbol: 'APPL', value: 195.46};
client.send("/topic/stocks", {}, JSON.stringify(quote));

client.subcribe("/topic/stocks", function(message) {
var quote = JSON.parse(message.body);
alert(quote.symbol + " is at " + quote.value);
});

事务支持
STOMP 客户端支持在发送消息时用事务进行处理:
举例说明:

// start the transaction
// 该方法返回一个包含了事务 id、commit()、abort() 的JavaScript 对象
var tx = client.begin();
// send the message in a transaction
// 最关键的在于要在 headers 对象中加入事务 id,若没有添加,则会直接发送消息,不会以事务进行处理
client.send("/queue/test", {transaction: tx.id}, "message in a transaction");
// commit the transaction to effectively send the message
tx.commit();
// tx.abort();

Debug 信息
STOMP 客户端默认将传输过程中的所有 debug 信息以 console.log() 形式输出到客户端浏览器中,也可通过以下方式输出到 DOM 中:

client.debug = function(str) {
    // str 参数即为 debug 信息
// append the debug log to a #debug div somewhere in the page using JQuery:
$("#debug").append(str + "\n");
};

认证
这一部分内容看的不是很理解,因此直接将原文放在这里了,待补充。

By default, STOMP messages will be automatically acknowledged by the server before the message is delivered to the client.

The client can chose instead to handle message acknowledgement by subscribing to a destination and specify a ack header set to client or client-individual.

In that case, the client must use the message.ack() method to inform the server that it has acknowledge the message.

var subscription = client.subscribe("/queue/test",
  function(message) {
    // do something with the message
    ...
    // and acknowledge it
    message.ack();
  },
  {ack: 'client'}
);
The ack() method accepts a headers argument for additional headers to acknowledge the message. For example, it is possible to acknowledge a message as part of a transaction and ask for a receipt when the ACK STOMP frame has effectively be processed by the broker: 
var tx = client.begin(); 
message.ack({ transaction: tx.id, receipt: ‘my-receipt’ }); 
tx.commit(); 
The nack() method can also be used to inform STOMP 1.1 brokers that the client did not consume the message. It takes the same arguments than the ack() method.
上一篇下一篇

猜你喜欢

热点阅读