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);
login和passcode是strings,connectCallback和errorCallback则是functions。(有些brokers(代理)还需要传递一个host(String类型)参数。)
有时候我们的连接没有专门的用户名和密码,都是在原有的已经认证的基础上进行连接,例如token,所以我们需要将token传给后端:这样我们需要一个http类似的请求头实现方式。stomp也支持hear的参数
- stompClient.connect(headers, connectCallback);
- stompClient.connect(headers, connectCallback, errorCallback);
header是map形式,connectCallback和errorCallback为functions。
需要注意:如果你使用上述这种方式,那就需要自行在headers添加login、passcode(甚至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的目的地。另外可以有两个可选的参数:headers,object类型包含额外的信息头部;body,一个String类型的参数。
stompClient.send(describetion, headers,messageBody)
stomp的数据订阅和接收是同时的,订阅方法需要传入一个数据处理回调函数,函数有且只有一个参数,参数中包含了服务器发送的数据。且该方法会返回一个Promise对象,用于取消数据订阅。
- //获取订阅结果用于取消订阅
- let pushSubscriptionPromise = stompClient.subscribe('subscribePath', message => {
- // 处理接收到的消息
- console.log('收到消息', message.body);
- });
数据的取消订阅使用订阅方法返回的对象调用unSubscribe()即可
pushUserSubscriptionPromise.unsubscribe()
在收到服务端的数据之后,我们可能需要给后端一个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>