vue实现stompjs+websocket和后端通信(二)

11 篇文章 0 订阅

        vue项目的搭建在这里就不说了,网上有很多教程。大家自行百度。前端使用stomp+websocket有很多的实现版本。这里只讲@stomp/stompjs方式实现。

导入

使用@stomp/stompjs需要执行安装命令:

import Stomp from '@stomp/stompjs';

 然后在组件中引入即可,这里需要注意的是,可能由于版本不一致,会导致引入的方式不一致。但是主要是以下两种方式中的一种

import { Client, Stomp} from '@stomp/stompjs';

或者
import Stomp from '@stomp/stompjs';

 连接服务器

        连接服务器需要用到connect()方法。该方法有一个基本的成功时的回调函数

var connect_success_callback = function() { 
        // called back after the client is connected and         
       authenticated to the STOMP server 
};

  connect()方法接受一个可选的参数(error_callback),当客户端不能连接上服务端时,这个回调函数error_callback会被调用,该函数的参数为对应的错误对象。

var error_callback = function(error) {
    // display the error's message header:
    alert(error.headers.message);
};
// 创建Stomp客户端实例
      let stompClient = Stomp.client('ws://localhost:7000/websocket-demo/stmpwebsocket');

      // 连接WebSocket
      stompClient.connect(frame => {
        console.log('Connected: ' + frame);
        //这里表示已经连接成功
      }, error => {
        console.error('STOMP error:', error);
      });

        connect()方法可以传入两个参数,用来做用户认证:用户的登录和密码凭证。在大多数情况下,connect()方法可接受不同数量的参数来提供简单的API:

stompClient.connect(login, passcode, connectCallback);

stompClient.connect(login, passcode, connectCallback, errorCallback); 

stompClient.connect(login, passcode, connectCallback, errorCallback, host);

  loginpasscode是strings,connectCallbackerrorCallback则是functions。(有些brokers(代理)还需要传递一个host(String类型)参数。)

        有时候我们的连接没有专门的用户名和密码,都是在原有的已经认证的基础上进行连接,例如token,所以我们需要将token传给后端:这样我们需要一个http类似的请求头实现方式。stomp也支持hear的参数

stompClient.connect(headers, connectCallback);
stompClient.connect(headers, connectCallback, errorCallback);

headermap形式,connectCallbackerrorCallback为functions。

需要注意:如果你使用上述这种方式,那就需要自行在headers添加loginpasscode(甚至host):

var headers = {
    login: 'username',
    passcode: 'mypasscode',
    token: 'my-client-id'
};
stompClient.connect(headers, connectCallback);

断开连接

        断开连接使用的时disconnect()方法,该方法接受一个回调函数

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

心跳保活

        如果STOMP broker(代理)接收STOMP 1.1版本的帧,heart-beating是默认启用的。

        heart-beating也就是频率,incoming是接收频率,outgoing是发送频率。通过改变incoming和outgoing可以更改客户端的heart-beating(默认为10000ms)。heart-beating是利用window.setInterval()去规律地发送heart-beats或者检查服务端的heart-beats。

stompClient.heartbeat.outgoing = 20000; 
// client will send heartbeats every 20000ms
stompClient.heartbeat.incoming = 0;
// client does not want to receive heartbeats
// from the server

 消息发送

        消息发送使用send()方法。这个方法必须有一个参数,用来描述对应的STOMP的目的地。另外可以有两个可选的参数:headersobject类型包含额外的信息头部;body,一个String类型的参数。

stompClient.send(describetion, headers,messageBody)

消息订阅和接收

         stomp的数据订阅和接收是同时的,订阅方法需要传入一个数据处理回调函数,函数有且只有一个参数,参数中包含了服务器发送的数据。且该方法会返回一个Promise对象,用于取消数据订阅。

//获取订阅结果用于取消订阅
      let pushSubscriptionPromise = stompClient.subscribe('subscribePath', message => {
        // 处理接收到的消息
        console.log('收到消息', message.body);
      });

取消订阅

        数据的取消订阅使用订阅方法返回的对象调用unSubscribe()即可

pushUserSubscriptionPromise.unsubscribe()

 消息确认ack

        在收到服务端的数据之后,我们可能需要给后端一个ack'消息,用于告诉后端我们已经收到了消息,这个机制在传输一些很重要的信息是有必要的。

//获取订阅结果用于取消订阅
      let pushSubscriptionPromise = stompClient.subscribe('subscribePath', message => {
        // 处理接收到的消息
        console.log('收到消息', message.body);
        / 发送ACK确认消息
        stompClient.ack(message);
      });

        stompClient.ack(message)方法用于发送一个ACK确认,表示我们已经收到并处理了ID为message.headers['message-id']的消息。如果你需要显式指定消息ID,可以这样做:

stompClient.ack(message.headers['message-id']);

以上就是使用@stomp/stompjs。

以下是一个完整的组件代码。可直接复制到一个已经搭建好的vue项目上测试。请忽略极其难看的ui页面。

<template>
  <div class="f_c float_l">
    <div class="m_10 float_l">
      <el-input class='input_common' v-model="websocketPath" placeholder="后端websocket地址:http://ip:port/context/websocket-point" ></el-input>
      <el-input class='input_common' v-model="userId" placeholder="请输入用户名" ></el-input>
      <el-button v-if="!connected" @click=" connectWebsocket" >连接</el-button>
      <el-button v-else @click="disConnectWebsocket" >断开</el-button>
    </div>
    <el-divider />
    <div class="f_c" v-if="connected">
      <div>
        <el-input class='input_common' v-model="messageUrl" placeholder="请输入消息地址:/sendMessage"></el-input>
        <el-button @click="sendMessage" v-if="connected">发送</el-button>
      </div>
      <el-input class='input_common mt_10' v-model="message" type="textarea" :rows="2" placeholder="请输入需要发送的消息"></el-input>
    </div>
    <el-divider />
    <div class="f_c m_10 float_l" v-if="connected">
      <div class="float_l">
        <el-input class='input_common' v-model="subscribePath" placeholder="请输入监听地址:/subscribe/id"></el-input>
        <el-button @click="subscribeTopic" v-if="!subscribed">广播订阅</el-button>
        <el-button @click="unSubscribeTopic" v-else>取消订阅</el-button>
      </div>
      <div class="m_10 f_c">
        <span class="float_l">收到消息</span>
        <span style="border: aqua; width: 500px; height: 100px">{{receiveMessage}}</span>
      </div>
    </div>
      <div class="f_c m_10 float_l" v-if="connected">
        <div class="float_l">
          <el-input class='input_common' v-model="userSubscribePath" placeholder="请输入监听地址:/subscribe/id"></el-input>
          <el-button @click="subscribeUserTopic" v-if="!userSubscribed">用户订阅</el-button>
          <el-button @click="unUserSubscribeTopic" v-else>取消订阅</el-button>
        </div>
        <div class="m_10 f_c">
          <span class="float_l">收到消息</span>
          <span style="border: aqua; width: 500px; height: 100px">{{receiveUserMessage}}</span>
        </div>
    </div>

  </div>
</template>

<script>
import { Client, Stomp} from '@stomp/stompjs';

export default {
  name: "index",
  data(){
    return {
      websocketPath:'localhost:7000/websocket-demo/stmpwebsocket',
      userId:'123456789',
      subscribePath:'/topic/targetSubscribe',
      receiveMessage:'',
      userSubscribePath:'/user/queue/targetUser',
      receiveUserMessage:'',
      messageUrl:'/message/stomp/springUserMessage/987654321',
      message:'',
      connected: false,
      subscribed: false,
      userSubscribed:false,
      stompClient:null,
      pushSubscriptionPromise: null,
      pushUserSubscriptionPromise: null
    }
  },
  methods:{
    connectWebsocket(){
      if(!this.websocketPath){
        this.$message.error("请先填写websocket地址信息")
        return
      }
      if(!this.userId){
        this.$message.error("请先填写用户信息")
        return
      }

      // 创建Stomp客户端实例
      this.stompClient = Stomp.client('ws://' + this.websocketPath);

      //请求头
      let headers = {
        'userId': this.userId
      }

      // 连接WebSocket
      this.stompClient.connect(headers, frame => {
        console.log('Connected: ' + frame);
        this.connected = true
      }, error => {
        console.error('STOMP error:', error);
      });
    },
    sendMessage(){
      if(!this.message || !this.messageUrl){
        this.$message.error("请先填写websocket地址和消息内容信息")
        return
      }
      let msg = { message: this.message };
      console.log("数据发送:", this.messageUrl, msg)
      this.stompClient.send(this.messageUrl, {'aaaaaa': 'aaaaaaaaa'}, JSON.stringify(msg));
    },
    onceSubscribe(){
      this.stompClient.subscribe('/message/subscribe/1232131321', function(message) {
        console.log('onceSubscribe-Message: ' + message.body);
      })
    },
    subscribeTopic(){
      if(!this.subscribePath){
        this.$message.error("请先填写订阅地址")
        return
      }
      // 订阅某个路径
      let number = 1
      this.subscribed = true
      //获取订阅结果用于取消订阅
      this.pushSubscriptionPromise = this.stompClient.subscribe(this.subscribePath, message => {
        // 处理接收到的消息
        console.log('收到消息', message);
        let nowMessage = '第' + number + '次收到订阅的消息:' + JSON.stringify(JSON.parse(message.body)) + '\r\n';
        this.receiveMessage += nowMessage;
        number ++;
      });
    },
    subscribeUserTopic(){
      if(!this.userSubscribePath){
        this.$message.error("请先填写用户的订阅地址")
        return
      }
      // 订阅某个路径
      let number = 1
      this.userSubscribed = true
      //获取订阅结果用于取消订阅
      this.pushUserSubscriptionPromise = this.stompClient.subscribe(this.userSubscribePath, message => {
        // 处理接收到的消息
        console.log('收到消息', message);
        let nowMessage = '第' + number + '次收到订阅的消息:' + JSON.stringify(JSON.parse(message.body)) + '\r\n';
        this.receiveUserMessage += nowMessage;
        number ++;
        // 发送ACK确认消息
        this. stompClient.ack(message);
      });
    },
    unSubscribeTopic(){
      // 订阅某个路径
      this.pushSubscriptionPromise.unsubscribe();
      this.subscribed = false;
      this.receiveMessage = ''
    },
    unUserSubscribeTopic(){
      // 订阅某个路径
      this.pushUserSubscriptionPromise.unsubscribe();
      this.userSubscribed = false;
      this.receiveUserMessage = ''
    },
    disConnectWebsocket(){
      if (this.stompClient != null) {
        // 关闭连接
        this.stompClient.disconnect()
        this.stompClient = null
        this.connected = false
        this.subscribed = false
        this.userSubscribed = false
        this.receiveMessage = ''
        this.receiveUserMessage = ''
      }
    },
    beforeDestroy(){
      if(this.stompClient != null){
        this.stompClient.disconnect()
        this.stompClient = null
        this.connected = false
        this.subscribed = false
        this.userSubscribed = false
        this.receiveMessage = ''
        this.receiveUserMessage = ''
      }
    }
  }
}
</script>

<style scoped>

</style>
  • 22
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基于 Spring Boot、WebSocketVue 实现后端实时向前端推送数据的代码示例: 1. 后端代码 ``` @Controller public class WebSocketController { private final WebSocketService webSocketService; @Autowired public WebSocketController(WebSocketService webSocketService) { this.webSocketService = webSocketService; } @GetMapping("/") public String index() { return "index"; } @Scheduled(fixedDelay = 1000) public void pushData() { webSocketService.sendAll(String.valueOf(System.currentTimeMillis())); } @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } } ``` ``` @Service public class WebSocketService { private final List<Session> sessions = new CopyOnWriteArrayList<>(); public void add(Session session) { sessions.add(session); } public void remove(Session session) { sessions.remove(session); } public void sendAll(String message) { sessions.forEach(session -> { try { session.getBasicRemote().sendText(message); } catch (IOException e) { e.printStackTrace(); } }); } } ``` 2. 前端代码 ``` <template> <div> <h1>Real-time data:</h1> <ul> <li v-for="(data, index) in dataList" :key="index">{{ data }}</li> </ul> </div> </template> <script> export default { data() { return { dataList: [] } }, mounted() { const ws = new WebSocket('ws://localhost:8080/ws'); ws.onmessage = (event) => { this.dataList.push(event.data); }; ws.onclose = () => { console.log('Connection closed'); }; } } </script> ``` 在这个示例中,我们在后端创建了一个定时任务,每秒钟向所有连接上的客户端推送当前时间戳。我们还创建了一个 WebSocketService,用于管理客户端连接和消息发送。 在前端,我们通过 Vue 的 mounted 生命周期创建了一个 WebSocket 连接,并在每次接收到服务器发来的消息时将其添加到一个数据列表中,然后在模板中通过 v-for 渲染出来。 要注意的是,我们在前端中只创建了一个 WebSocket 连接,用于接收服务器推送的数据。这是因为 WebSocket 是全双工通信,可以同时进行发送和接收操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值