本篇文章主要就是记录一下前后端如何集成STOMP连接
- 后端是通过java+springboot+Stomp
- 前端是sockJS+Stomp
最原始的需求是在cocos里面使用长连接进行消息通信,毕竟游戏的场景下,服务端通知客户端是很常见的,用http满足不了,只能使用socket进行通信。
简单介绍一下Stomp(Simple Text Oriented Messaging Protocol),是一种基于websocket之上的简洁通信协议。
怎么选择的stomp呢
作为java开发,那肯定是springboot了,新建springboot项目
在组件依赖里面选择websocket,组件的简介看完大家应该明白了,就是集成了SockJS和STOMP,这里说一下,springboot的版本使用2.x,3.x要求jdk17,我本地装的老古董jdk1.8,所以只能2.x
这里的两个链接很重要,第一个是简单的教程地址,第二个是详细的教程地址,我就是从这个地址开始自学的(主要是百度搜出来的教程太乱了,看多了反而更糊涂)
- Getting Started | Using WebSocket to build an interactive web application (spring.io)
- Web on Servlet Stack
回答一下小标题,怎么选中的stomp呢,主要就是发现springboot已经集成了stomp,本着拿来主义精神,选了stomp。
简易教程
这里的简易教程就是把官方的简易教程实现了一下
第一步:配置maven
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator-core</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>sockjs-client</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>stomp-websocket</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>3.3.7</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.1.1-1</version>
</dependency>
如果只是作为服务端,那么spring-boot-starter-websocket这个jar包是必须的,其他的jar都是为了示例,因为前后端放在一起了,其他包都是前端需要的
第二步:添加config
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
/**
* websocket的配置类
* @author dfg
* @date 2023-04-28 21:50
*/
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
// 客户端接收服务器消息的地址前缀
config.enableSimpleBroker("/topic");
// 客户端给服务端发消息的地址前缀
config.setApplicationDestinationPrefixes("/app");
}
/**
* 注册端点
* setAllowedOrigins 支持跨域
* withSockJS 支持socketJs
* @param registry
*/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/gs-guide-websocket").setAllowedOriginPatterns("*").withSockJS();
}
}
注释已经写清楚了,只是这里有一个知识点需要了解一下,一直在说Stomp,那他到底是怎么通信的呢
这是官方的原理图,解读一下就两条线和两个很重要的类
- 客户端发送目的地是/app/a的消息,服务端收到之后发现是/app的消息,给到SimpAnnotationMethod类处理,返回一个目的地是/topic/a的消息,给到SimpleBroker类,查找所有订阅了/topic/a这个目的地的客户端,广播消息
- 客户端发送目的地是/topic/a的消息,服务端收到之后一看是/topic消息,直接给到SimpleBroker类,然后查找&广播一样的流程
- SimpAnnotationMethodMessageHandler:接收消息的处理类
- SimpleBrokerMessageHandler:发送消息的处理类
了解了原理,在看配置信息就清晰了
第三步:编写controller
package com.example.messagingstompwebsocket;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
import org.springframework.web.util.HtmlUtils;
@Controller
public class GreetingController {
@MessageMapping("/hello")
@SendTo("/topic/greetings")
public Greeting greeting(HelloMessage message) throws Exception {
Thread.sleep(1000); // simulated delay
return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");
}
}
看到上述代码,了解web编程的应该很熟悉了,只是返回结果是一个广播的形式,订阅了的客户端都能收到,上述两个重要注解@MessageMapping("/hello")最终接收得目的地是/app/hello,@SendTo("/topic/greetings")发送的目的地就是/topic/greetings
第四步:前端代码
完整部分自行看教程,我把重要部分复制了出来
// 引入sockjs与stomp
<script src="/webjars/sockjs-client/sockjs.min.js"></script>
<script src="/webjars/stomp-websocket/stomp.min.js"></script>
// 连接
function connect() {
var socket = new SockJS('/gs-guide-websocket');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
setConnected(true);
console.log('Connected: ' + frame);
// 订阅目的地是/topic/greetings的消息
stompClient.subscribe('/topic/greetings', function (greeting) {
showGreeting(JSON.parse(greeting.body).content);
});
});
}
// 发送目的地是/app/hello的消息
function sendName() {
stompClient.send("/app/hello", {}, JSON.stringify({'name': $("#name").val()}));
}
最后介绍一种用法
后端的controller
package com.example.messagingstompwebsocket;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
import org.springframework.web.util.HtmlUtils;
@Controller
public class GreetingController {
@MessageMapping("/hello/{groupId}")
@SendTo("/topic/greetings/{groupId}")
public Greeting greeting(@DestinationVariable String groupId, HelloMessage message) throws Exception {
Thread.sleep(1000); // simulated delay
return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");
}
}
注解里面支持占位符,例如是群组聊天,通过生成一个groupId,给组里面的所有人发送消息