STOMP 客户端 API 整理

本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jqsad/article/details/77745379

参考教程:http://jmesnil.net/stomp-websocket/doc/

在使用 stomp.js 时,能找到的较完整的 API 说明基本都是英文,中文资料比较少,因此,参考上边的教程做了以下的笔记和总结

介绍

STOMP(Simple Text-Orientated Messaging Protocol) 面向消息的简单文本协议

WebSocket是一个消息架构,不强制使用任何特定的消息协议,它依赖于应用层解释消息的含义;

与处在应用层的HTTP不同,WebSocket处在TCP上非常薄的一层,会将字节流转换为文本/二进制消息,因此,对于实际应用来说,WebSocket的通信形式层级过低,因此,可以在 WebSocket 之上使用 STOMP协议,来为浏览器 和 server间的 通信增加适当的消息语义。

如何理解 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>
  • 1

发起连接

客户端可以通过使用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");

2) 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 itmessage.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.

1、JavaScript 依赖STOMP 依赖 sockjs.js 和 stomp.min.js。stomp.min.js的下载链接:http://www.bootcdn.cn/stomp.js/<script type="text/javascript" src="http://cdn.bootcss.com/sockjs-client/1.1.1/sockjs.js"></script><script type="text/javascript" src="/js/stomp.min.js"></script>2、JavaScript 客户端实现[点击并拖拽以移动]/*STOMP*/
var url = 'http://localhost:8080/stomp';
var sock = new SockJS(url);
var stomp = Stomp.over(sock);var strJson = JSON.stringify({'message': 'Marco!'});//默认的和STOMP端点连接
/*stomp.connect("guest", "guest", function (franme) {});*/var headers={username:'admin',password:'admin'
};stomp.connect(headers, function (frame) {//发送消息//第二个参数是一个头信息的Map,它会包含在STOMP的帧中//事务支持var tx = stomp.begin();stomp.send("/app/marco", {transaction: tx.id}, strJson);tx.commit();//订阅服务端消息 subscribe(destination url, callback[, headers])stomp.subscribe("/topic/marco", function (message) {var content = message.body;var obj = JSON.parse(content);console.log("订阅的服务端消息:" + obj.message);}, {});stomp.subscribe("/app/getShout", function (message) {var content = message.body;var obj = JSON.parse(content);console.log("订阅的服务端直接返回的消息:" + obj.message);}, {});/*以下是针对特定用户的订阅*/var adminJSON = JSON.stringify({'message': 'ADMIN'});/*第一种*/stomp.send("/app/singleShout", {}, adminJSON);stomp.subscribe("/user/queue/shouts",function (message) {var content = message.body;var obj = JSON.parse(content);console.log("admin用户特定的消息1:" + obj.message);});/*第二种*/stomp.send("/app/shout", {}, adminJSON);stomp.subscribe("/user/queue/notifications",function (message) {var content = message.body;var obj = JSON.parse(content);console.log("admin用户特定的消息2:" + obj.message);});/*订阅异常消息*/stomp.subscribe("/user/queue/errors", function (message) {console.log(message.body);});//若使用STOMP 1.1 版本,默认开启了心跳检测机制(默认值都是10000ms)stomp.heartbeat.outgoing = 20000;stomp.heartbeat.incoming = 0; //客户端不从服务端接收心跳包
});

STOMP 服务端整理

一、STOMP 简介

直接使用WebSocket(或SockJS)就很类似于使用TCP套接字来编写Web应用。因为没有高层级的线路协议(wire protocol),因此就需要我们定义应用之间所发送消息的语义,还需要确保连接的两端都能遵循这些语义。

就像HTTP在TCP套接字之上添加了请求-响应模型层一样,STOMP在WebSocket之上提供了一个基于帧的线路格式(frame-based wire format)层,用来定义消息的语义。

与HTTP请求和响应类似,STOMP帧由命令、一个或多个头信息以及负载所组成。例如,如下就是发送数据的一个STOMP帧:

>>> SEND
transaction:tx-0
destination:/app/marco
content-length:20{"message":"Marco!"}

在这个例子中,STOMP命令是send,表明会发送一些内容。紧接着是三个头信息:一个表示消息的的事务机制,一个用来表示消息要发送到哪里的目的地,另外一个则包含了负载的大小。然后,紧接着是一个空行,STOMP帧的最后是负载内容。

1、启用STOMP功能

STOMP 的消息根据前缀的不同分为三种。如下,以 /app 开头的消息都会被路由到带有@MessageMapping 或 @SubscribeMapping 注解的方法中;以/topic 或 /queue 开头的消息都会发送到STOMP代理中,根据你所选择的STOMP代理不同,目的地的可选前缀也会有所限制;以/user开头的消息会将消息重路由到某个用户独有的目的地上。

View Code

2、处理来自客户端的STOMP消息

服务端处理客户端发来的STOMP消息,主要用的是 @MessageMapping 注解。如下:

  @MessageMapping("/marco")@SendTo("/topic/marco")public Shout stompHandle(Shout shout){LOGGER.info("接收到消息:" + shout.getMessage());Shout s = new Shout();s.setMessage("Polo!");return s;}

2.1、@MessageMapping 指定目的地是“/app/marco”(“/app”前缀是隐含的,因为我们将其配置为应用的目的地前缀)。

2.2、方法接收一个Shout参数,因为Spring的某一个消息转换器会将STOMP消息的负载转换为Shout对象。Spring 4.0提供了几个消息转换器,作为其消息API的一部分:

2.3、尤其注意,这个处理器方法有一个返回值,这个返回值并不是返回给客户端的,而是转发给消息代理的,如果客户端想要这个返回值的话,只能从消息代理订阅。@SendTo 注解重写了消息代理的目的地,如果不指定@SendTo,帧所发往的目的地会与触发处理器方法的目的地相同,只不过会添加上“/topic”前缀。

2.4、如果客户端就是想要服务端直接返回消息呢?听起来不就是HTTP做的事情!即使这样,STOMP 仍然为这种一次性的响应提供了支持,用的是@SubscribeMapping注解,与HTTP不同的是,这种请求-响应模式是异步的...

   @SubscribeMapping("/getShout")public Shout getShout(){Shout shout = new Shout();shout.setMessage("Hello STOMP");return shout;}

3、发送消息到客户端

3.1 在处理消息之后发送消息

正如前面看到的那样,使用 @MessageMapping 或者 @SubscribeMapping 注解可以处理客户端发送过来的消息,并选择方法是否有返回值。

如果 @MessageMapping 注解的控制器方法有返回值的话,返回值会被发送到消息代理,只不过会添加上"/topic"前缀。可以使用@SendTo 重写消息目的地;

如果 @SubscribeMapping 注解的控制器方法有返回值的话,返回值会直接发送到客户端,不经过代理。如果加上@SendTo 注解的话,则要经过消息代理。

3.2 在应用的任意地方发送消息

spring-websocket 定义了一个 SimpMessageSendingOperations 接口(或者使用SimpMessagingTemplate ),可以实现自由的向任意目的地发送消息,并且订阅此目的地的所有用户都能收到消息。

  @Autowiredprivate SimpMessageSendingOperations simpMessageSendingOperations;/*** 广播消息,不指定用户,所有订阅此的用户都能收到消息* @param shout*/@MessageMapping("/broadcastShout")public void broadcast(Shout shout) {simpMessageSendingOperations.convertAndSend("/topic/shouts", shout);}

3.3 为指定用户发送消息

3.2介绍了如何广播消息,订阅目的地的所有用户都能收到消息。如果消息只想发送给特定的用户呢?spring-websocket 介绍了两种方式来实现这种功能,一种是 基于@SendToUser注解和Principal参数,一种是SimpMessageSendingOperations 接口的convertAndSendToUser方法。

  • 基于@SendToUser注解和Principal参数

@SendToUser 表示要将消息发送给指定的用户,会自动在消息目的地前补上"/user"前缀。如下,最后消息会被发布在  /user/queue/notifications-username。但是问题来了,这个username是怎么来的呢?就是通过 principal 参数来获得的。那么,principal 参数又是怎么来的呢?需要在spring-websocket 的配置类中重写 configureClientInboundChannel 方法,添加上用户的认证。

spring-websocket 用户认证

  @MessageMapping("/shout")@SendToUser("/queue/notifications")public Shout userStomp(Principal principal, Shout shout) {String name = principal.getName();String message = shout.getMessage();LOGGER.info("认证的名字是:{},收到的消息是:{}", name, message);return shout;}
  • convertAndSendToUser方法

    除了convertAndSend()以外,SimpMessageSendingOperations 还提供了convertAndSendToUser()方法。按照名字就可以判断出来,convertAndSendToUser()方法能够让我们给特定用户发送消息。

    @MessageMapping("/singleShout")public void singleUser(Shout shout, StompHeaderAccessor stompHeaderAccessor) {String message = shout.getMessage();LOGGER.info("接收到消息:" + message);Principal user = stompHeaderAccessor.getUser();simpMessageSendingOperations.convertAndSendToUser(user.getName(), "/queue/shouts", shout);}

如上,这里虽然我还是用了认证的信息得到用户名。但是,其实大可不必这样,因为 convertAndSendToUser 方法可以指定要发送给哪个用户。也就是说,完全可以把用户名的当作一个参数传递给控制器方法,从而绕过身份认证!convertAndSendToUser 方法最终会把消息发送到 /user/sername/queue/shouts 目的地上。

4、处理消息异常

在处理消息的时候,有可能会出错并抛出异常。因为STOMP消息异步的特点,发送者可能永远也不会知道出现了错误。@MessageExceptionHandler标注的方法能够处理消息方法中所抛出的异常。我们可以把错误发送给用户特定的目的地上,然后用户从该目的地上订阅消息,从而用户就能知道自己出现了什么错误啦...

     @MessageExceptionHandler(Exception.class)@SendToUser("/queue/errors")public Exception handleExceptions(Exception t){t.printStackTrace();return t;}

STOMP和WebSocket相关推荐

  1. SpringBoot WebSocket Stomp

    关注 "弋凡"(YiFan)微信公众号吧 记录简单笔记 做你的最爱 websocket 是什么? 是一种网络通信协议,很多高级功能都需要它 为什么要使用websocket? 已经有 ...

  2. springmvc(18)使用WebSocket 和 STOMP 实现消息功能

    [0]README 1)本文旨在 介绍如何 利用 WebSocket 和 STOMP 实现消息功能: 2)要知道, WebSocket 是发送和接收消息的 底层API,而SockJS 是在 WebSo ...

  3. 使用ActiveMQ和HornetQ通过WebSocket通过STOMP轻松进行消息传递

    消息传递是用于构建不同级别的分布式软件系统的极其强大的工具. 通常,至少在Java生态系统中,客户端(前端)从不直接与消息代理(或交换)进行交互,而是通过调用服务器端(后端)服务来进行交互. 否则,客 ...

  4. WebSocket子协议STOMP详解

    为什么80%的码农都做不了架构师?>>>    1. STOMP简介 STOMP即Simple (or Streaming) Text Orientated Messaging Pr ...

  5. Spring使用WebSocket、SockJS、STOMP实现消息功能

    WebSocket 概述 WebSocket协议提供了通过一个套接字实现全双工通信的功能.除了其他的功能之外,它能够实现Web浏览器和服务器之间的异步通信.全双工意味着服务器可以发送消息给浏览器,浏览 ...

  6. vue+websocket+Stomp组件实现前端长连接

    1文件结构 2.重点文件夹中的文件代码以及作用 ① 根目录中systemConfig文件夹中的main.js文件 /***作用:作为项目中的配置文件,放了websocket连接的url,用户名(adm ...

  7. c语言实现stomp协议客户端,在vue中使用SockJS实现webSocket通信

    最近接到一个业务需求,需要做一个聊天信息的实时展示的界面,这就需要和 服务器 端建立webSocket连接,从而实现数据的实时获取和视图的实时刷新.在此将我的实现记录下来,希望可以给有同样需求的人一些 ...

  8. Android WebSocket长链接使用Stomp协议【精品】

    这是Android WebSocket客户端监听的使用 1.安卓没有适配Stom协议的WebSocket监听,所以只能自己进行Okhttp封装 2.以下是借助Okhttp和Stomp进行WebSock ...

  9. Springboot+Websocket+Stomp实现PC兼容,及微信小程序连接

    导入maven依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId> ...

最新文章

  1. python 命令行参数-python中命令行参数
  2. 好程序员web前端分享JS引擎的执行机制
  3. WPF中打印问题的探讨[转]
  4. WenLan-10亿参数!别只玩GPT,来看看人大中科院联手打造第一个大规模多模态中文预训练模型BriVL...
  5. 小学音乐教学和计算机的融合,【多媒体技术论文】小学音乐多媒体教学融入策略问题(共5881字)...
  6. 债务回收解决方案行业调研报告 - 市场现状分析与发展前景预测(2021-2027年)
  7. linux下安装mongodb,以及解决安装报错问题
  8. 【C++】字符串替换问题
  9. Vscode关闭自动更新
  10. 【Operamasks-UI】可编辑表格insertRow和deleteRow遇到空表格时的问题
  11. 【ZFNet】Visualizing and Understanding Convolutional Networks (2013) 全文翻译
  12. 启动nodejs时报错 internal/modules/cjs/loader.js:584的解决办法
  13. 如何用英文说明一种方法的优缺点(优点和缺点)
  14. 广陵郡(堂)追宗溯祖与世系排行
  15. 在vs2008中编译Secret Maryo Chronicles
  16. 构建银行人工智能用户画像和自动营销体系
  17. 01背包.动态规划.c语言实现
  18. 函数的梯度方向和切线方向_方向导数和梯度是什么?
  19. 中国乳酸菌制品市场销售状况及营销趋势预测报告(2022-2027年)
  20. kingdee漏洞金蝶EAS存在命令执行漏洞

热门文章

  1. LiveVideoStackCon 2018演讲视频发布
  2. Windows 安装 Podman Desktop
  3. 8.5 JVM命令行监控——jmap
  4. 【Windows】局域网内共享文件夹的设置方法
  5. 有刷电机的速度pid-位置pid算法
  6. html+js开发windows桌面程序的方案(miniblink)
  7. mongodb--复制集和故障诊断(笔记)
  8. 3ds max 到处3ds_3ds是否仍然有意义
  9. 合理的睡眠时间很重要(转)
  10. 2019/12/15周末日志