Spring Boot——整合WebSocket(基于STOMP
2019-07-09 本文已影响80人
Hi_JIAQI
WebSocket 与HTTP 的简单区别:
- HTTP 每次请求应答都需要客户端与服务端建立连接的模式;
- HTTP 是一种由客户端到服务端的单向通信协议;
- .WebSocket 是一种双向通信协议,WebSocket 服务器和Browser/Client Agent 都能主动的向对方发送或接收数据;
- 两种请求的方式不同,一种是ws(wss)另一种是http(https)
WebSocket 简单使用:
1.添加pom文件依赖:
<!--websocket-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2.直接上源码:
@Repository
public class MyHandler extends TextWebSocketHandler {
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
System.out.println("获取到消息》》》》" + message.getPayload());
session.sendMessage(new TextMessage("消息已收到"));
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
session.sendMessage(new TextMessage("欢迎连接到ws服务"));
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
System.out.println("断开连接");
}
}
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Autowired
MyHandler myHandler;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
webSocketHandlerRegistry.addHandler(this.myHandler,"/ws").setAllowedOrigins("*");
}
}
3.我们可以通过在线测试工具进行测试:
基于STOMP协议的WebSocket的使用:
它提供了一个可互操作的连接格式,允许STOMP客户端与任意STOMP消息代理(Broker)进行交互,类似于OpenWire(一种二进制协议)
STOMP协议工作于TCP协议之上,使用了下列命令:
- SEND 发送
- SUBSCRIBE 订阅
- UNSUBSCRIBE 退订
- DISCONNECT 断开
1.一样先WebSocket的添加pom文件依赖
2.配置websocket stomp(代码中均有做解释)
/**
* 通过EnableWebSocketMessageBroker
* 开启使用STOMP协议来传输基于代理(message broker)的消息,
* 此时浏览器支持使用@MessageMapping 就像支持@RequestMapping一样。
*/
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
/**
* 注册stomp的端点
*/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
//endPoint 注册协议节点,并映射指定的URl点对点-用
//注册一个名字为"endpointChat" 的endpoint,并指定 SockJS协议。
//允许使用socketJs方式访问,访问点为webSocketServer,允许跨域
registry.addEndpoint("/endpointChat")
.setAllowedOrigins("*")
.withSockJS();
}
/**
* 配置消息代理(message broker)
* @param registry
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {//配置消息代理(message broker)
//订阅Broker名称:topic 代表发布广播,即群发
//queue 代表点对点,即发指定用户
registry.enableSimpleBroker("/queue", "/topic");
// 全局使用的消息前缀(客户端订阅路径上会体现出来)
registry.setApplicationDestinationPrefixes("/app");
//点对点使用的订阅前缀(客户端订阅路径上会体现出来),
// 不设置的话,默认也是/user/
// registry.setUserDestinationPrefix("/user/");
}
}
3.消息实体类
客户端发往服务器端实体类(可自定义)
public class RequestMessage {
private String name;
public String getName() {
return name;
}
}
服务器端发往客户端实体类(可自定义)
public class ResponseMessage {
private String responseMessage;
public String getResponseMessage() {
return responseMessage;
}
public void setResponseMessage(String responseMessage) {
this.responseMessage = responseMessage;
}
public ResponseMessage() {
}
public ResponseMessage(String responseMessage) {
this.responseMessage = responseMessage;
}
}
4.控制层
@Controller
public class WebSockedController {
/**
* @param requestMessage
* @return
* @MessageMapping 指定要接收消息的地址,类似@RequestMapping。除了注解到方法上,也可以注解到类上
* @SendTo默认 消息将被发送到与传入消息相同的目的地
*/
@MessageMapping("/sendTest")
@SendTo("/topic/subscribeTest")
public ResponseMessage broadcast(RequestMessage requestMessage) {
ResponseMessage responseMessage = new ResponseMessage();
responseMessage.setResponseMessage("你发送的消息为:" + requestMessage.getName());
return responseMessage;
}
@SubscribeMapping("/subscribeTest")
public ResponseMessage sub() {
ResponseMessage responseMessage = new ResponseMessage();
responseMessage.setResponseMessage("感谢你订阅了我");
return responseMessage;
}
@Autowired
SimpMessagingTemplate template;
@GetMapping("/indexaaa")
@ResponseBody
public Result gg() {
template.convertAndSend("/topic/getResponse", new ResponseMessage("通过SimpMessagingTemplate进行消息广播该方式无法直接被使用,需要建立连接后访问使用,"));
return Result.success();
}
@RequestMapping(value = "/index")
public String broadcastIndex(HttpServletRequest req) {
System.out.println(req.getRemoteHost());
return "index";
}
}
5.前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>测试</title>
</head>
<body>
<h1>websocket!!!!</h1>
<br/><input id="text" type="text"/>
<button onclick="send()">发送消息</button>
<button onclick="subscribe1()">订阅消息/topic/subscribeTest</button>
<hr/>
<button onclick="closeWebSocket()">关闭WebSocket连接</button>
<hr/>
<div id="message"></div>
</body>
<script src="https://cdn.bootcss.com/jquery/2.1.4/jquery.min.js"></script>
<script src="http://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js"></script>
<script src="https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js"></script>
<script>
// websocket的连接地址:表示连接的SockJS的endpoint名称为/endpointChat
var socket = new SockJS("/endpointChat");
// 使用STOMP来创建WebSocket客户端
var stompClient = Stomp.over(socket);
//调用stompClient中的connect方法来连接服务端
// 向服务器发起websocket连接并发送CONNECT帧
stompClient.connect({}, function connectCallback(frame) {
// 连接成功时(服务器响应 CONNECTED 帧)的回调方法
setMessageInnerHTML("连接成功");
//调用stompClient中的subscribe方法来订阅/topic/getResponse发送来的消息
//也就是我们在Controller中的say方法上添加的@SendTo注解的参数。
//stompClient中的send方法表示发送一条消息到服务端,
// 客户端订阅消息的目的地址:此值BroadcastCtl中被@SendTo("/topic/subscribeTest")注解的里配置的值
stompClient.subscribe('/topic/getResponse', function (response) {
console.log(response);
var returnData = JSON.parse(response.body);
setMessageInnerHTML("/topic/getResponse 你接收到的消息为:" + returnData.responseMessage);
});
},
function errorCallBack(error) {
// 连接失败时(服务器响应 ERROR 帧)的回调方法
setMessageInnerHTML("连接失败");
}
);
//发送消息
function send() {
// 客户端消息发送的目的:服务端使用BroadcastCtl中
// @MessageMapping("/sendTest")注解的方法来处理发送过来的消息
var message = document.getElementById('text').value;
var messageJson = JSON.stringify({"name": message});
stompClient.send("/app/sendTest", {}, messageJson);
setMessageInnerHTML("/app/sendTest 你发送的消息:" + message);
}
//订阅消息
function subscribe1() {
stompClient.subscribe('/app/subscribeTest', function (response) {
setMessageInnerHTML("已成功订阅/topic/subscribeTest1");
var returnData = JSON.parse(response.body);
setMessageInnerHTML("/topic/subscribeTest 你接收到的消息为:" + returnData.responseMessage);
});
}
//将消息显示在网页上
function setMessageInnerHTML(innerHTML) {
document.getElementById('message').innerHTML += innerHTML + '<br/>';
}
//断开
function closeWebSocket() {
if (stompClient != null) {
stompClient.disconnect();
}
setMessageInnerHTML("关闭连接成功");
}
</script>
</html>
6.功能详解
@MessageMapping(“/sendTest”)
接收客户端发送的消息,当客户端发送消息的目的地为/app/sendTest时,交给该注解所在的方法处理消息,其中/app是在WebSocketConfig配置文件configureMessageBroker方法中添加:
registry.setApplicationDestinationPrefixes("/app");
若没有添加@SendTo注解且该方法有返回值,则返回的目的地地址为/topic/sendTest,经过消息代理,客户端需要订阅了这个主题才能收到返回消息
@SubscribeMapping(“/subscribeTest”)
接收客户端发送的订阅,当客户端订阅的目的地为/app/subscribeTest时,交给该注解所在的方法处理订阅,其中/app为客户端请求前缀
若没有添加@SendTo注解且该方法有返回值,则返回的目的地地址为/app/sendTest,不经过消息代理,客户端需要订阅了这个主题才能收到返回消息
@SendTo(“/topic/subscribeTest”)
修改返回消息的目的地地址为/topic/subscribeTest,经过消息代理,客户端需要订阅了这个主题才能收到返回消息