vue使用SockJS实现webSocket通信

以前使用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.
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,056评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,842评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,938评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,296评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,292评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,413评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,824评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,493评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,686评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,502评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,553评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,281评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,820评论 3 305
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,873评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,109评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,699评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,257评论 2 341

推荐阅读更多精彩内容