SpringBoot - 整合WebSocket及STOMP部分API简介

WebSocket简介

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

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

理解STOMP和WebSocket之间的关系

1、直接使用WebSocket(SockJS)就很类似于使用TCP套接字来编写web应用,因为没有高层协议,就需要我们定义应用间所发送消息的语义,还需要确保连接的两端都能遵循这些语义。
2、同HTTP在TCP套接字上添加请求-响应模型层一样,STOMP在WebSocket之上提供了一个基于帧的线路格式层,用来定义消息语义。

消息群发 - 发布模式

一、相关依赖引入
1、后端pom依赖
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2、前端依赖
<!-- 前端库 -->
<dependency>
	<groupId>org.webjars</groupId>
	<artifactId>webjars-locator-core</artifactId>
</dependency>
<dependency>
	<groupId>org.webjars</groupId>
	<artifactId>sockjs-client</artifactId>
	<version>1.1.2</version>
</dependency>
<dependency>
	<groupId>org.webjars</groupId>
	<artifactId>stomp-websocket</artifactId>
	<version>2.3.3</version>
</dependency>
<dependency>
	<groupId>org.webjars</groupId>
	<artifactId>jquery</artifactId>
	<version>3.3.1</version>
</dependency>

也可以直接前端页面引入相关JS文件

jquery的js
sockjs-client的js
stomp-websocket的js
二、WebSocket配置文件

Spring提供了基于WebSocketSTOMP支持,STOMP是一个简单的可互操作的协议,通常用于中间服务器与客户端之间进行异步消息传递

@Configuration
@EnableWebSocketMessageBroker    // 开启WebSocket消息代理
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        /**
         * 设置消息代理的前缀 - '/topic'
         * 被设置的前缀的消息会被转发到消息代理
         * 消息代理再将消息广播给当前连接的客户端 - 群发
         */
        registry.enableSimpleBroker("/topic");
        /**
         * 配置目标前缀,这里只配置一个,即/app
         * 配置了的前缀为/app可以通过@MessageMapping注解的方法处理
         * 其他的destination如/topic、/queue将被直接交给broker处理
         */
        registry.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        /**
         * 设置前缀为/chat,可以通过这个/chat建立连接
         * withSockJs支持解决WbeSocket兼容问题
         */
        registry.addEndpoint("/chat").withSockJS();
    }
}
三、创建消息Pojo类
@Data
public class Message {
    /** 昵称 */
    private String name;
    /** 消息内容 */
    private String content;
}

三、Controller层

这里的Controller主要是用来处理消息的,前面配置文件中,我们配置了/app目标前缀,前缀为/app的会进入到我们下面所说的Controller层里标注@MessageMapping的方法里被处理。

@Controller
public class TestController {
    @MessageMapping("/hello")          // 接收/app/hello路径发来的消息
    @SendTo("/topic/greetings")        // 转发到/topic/greetings
    public Message greeting(Message message){
        return message;
    }
}

除了@SendTo注解可以将处理过的消息转发到broker,再由broker进行消息广播外。Spring提供了一个SimpMessagingTemplate类来让开发者更加灵活地发送消消息。

@Autowired
private SimpMessagingTemplate template;

@MessageMapping("/hello")          // 接收/app/hello路径发来的消息
public void greeting(Message message){
    template.convertAndSend("/topic/greetings",message);
}
四、前端界面及接受和发送消息
1、前端页面
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>WebSocket群聊</title>
</head>
<body>
<!-- 用户名区域 -->
<div>
    <label for="name">请输入用户名:</label>
    <input type="text" id="name" placeholder="用户名">
</div>
<!-- 连接区域 -->
<div>
    <button id="connect" type="button">连接</button>
    <button id="disconnect" type="button" disabled="disabled">断开连接</button>
</div>
<!-- 发送消息区域 -->
<div id="chat" style="display:none">
    <div>
        <label for="content">请输入聊天内容:</label>
        <input type="text" id="content" placeholder="聊天内容">
    </div>
    <button id="send" type="button">发送</button>
</div>
<!-- 聊天区域 -->
<div id="greetings">
    <div id="conversation" style="display: none">群聊中...</div>
</div>
<!-- JS引用区域 -->
<script src="/webjars/jquery/jquery.min.js"></script>
<script src="/webjars/sockjs-client/sockjs.min.js"></script>
<script src="/webjars/stomp-websocket/stomp.min.js"></script>
<!-- 自定义JS -->
<script src="/js/app.js"></script>
</body>
</html>
2、自定义JS
// STOMP客户端
var stompClient = null;
/**
 * 是否已经连接,对页面显示进行处理
 * @param connected 是否已经连接
 */
function setConnected(connected) {
    $("#connect").prop("disabled",connected);
    $("#disconnect").prop("disabled",!connected);
    if(connected){
        $("#conversation").show();
        $("#chat").show();
    }else{
        $("#conversation").hide();
        $("#chat").hide();
    }
    $("#greetings").html("");
}

/**
 * 建立WebSocket连接
 */
function connect() {
    // 如果名字为空,则不让连接
    if(!$("#name").val()){
        return;
    }
    // 通过SockJs建立连接对象
    var socket = new SockJS('/chat');

    // 也可以通过WebSocket建立连接
    // var socket = new WebSocket("/chat");

    // 获取STOMP子协议的客户端对象
    stompClient = Stomp.over(socket);
    // 向服务器发起websocket连接并发送CONNECT镇
    stompClient.connect({},function (frame) {
        // 表示连接成功,
        setConnected(true);
        // 订阅服务端发送的消息
        stompClient.subscribe('/topic/greetings',function (greeting) {
            // 显示消息
            showGreeting(JSON.parse(greeting.body));
        });
    });
}

/**
 * 断开连接
 */
function disconnect() {
    if (stompClient != null){
        stompClient.disconnect();
    }
    setConnected(false);
}

/**
 * 发送消息
 */
function sendMessage() {
    // 发送消息
    stompClient.send("/app/hello",{},
        JSON.stringify({'name':$("#name").val(),'content':$("#content").val()}));
}

/**
 * 控制显示消息
 * @param message 消息对象
 */
function showGreeting(message) {
    $("#greetings").append("<div>" + message.name + ":" + message.content + "</div>");
}

$(function () {
   $("#connect").click(function () {
       connect();
   });
   $("#disconnect").click(function () {
       disconnect();
   });
   $("#send").click(function () {
       sendMessage();
       // 清空聊天框
       $("#content").val("");
   })
});
五、测试

运行项目,用两个不同的浏览器打开,分别发送消息。
消息

STOMP中的API介绍

app.js中的STOMP的API解释。

一、发起连接
1、Socket连接对象
/**
 * 通过SockJS建立WebSocket连接对象
 * 参数就是我们配置文件中registerStompEndpoints方法里配置的前缀
 */
var socket = new SockJS('/chat');
// 同样,可以用下面这行代码代替,但是就没有SockJS提供的兼容支持
var socket = new WebSocket("/chat");
2、签名方法
// 获取STOMP子协议的客户端对象
var stompClient = Stomp.over(socket);
/**
 * headers:客户端的认证信息
 * connectCallback:连接成功时(服务器响应 CONNECTED 帧)的回调方法
 * errorCallback:连接失败时(服务器响应 CONNECTED 帧)的回调方法
 * 如果不需要认证,使用{}替代即可
 * 失败回调方法可以省略
 */
stompClient.connect(headers, connectCallback, errorCallback);

其中headers认证信息大概长这个样子:

var headers = {
  login: 'mylogin',
  passcode: 'mypasscode',
  // additional header
  'client-id': 'my-client-id'
};
二、断开连接

断开连接是异步操作的。

/**
 * disconnectCallback:回调方法
 * 回调方法在操作完成时调用,可以省略
 */
stompClient.disconnect(disconnectCallback);
三、心跳机制

STOMP 1.1 版本,默认开启了心跳检测机制,可通过client对象的heartbeat进行配置,默认是10000ms

stompClient.heartbeat.outgoing=20000;
stompClient.heartbeat.incoming=0;
四、发送消息
/**
 * destinationUrl:服务端Controller中@MessageMapping中匹配的URL
 * headers:发送信息的header,JavaScript对象,可选参数,省略可用{}代替
 * body:发送信息的body,字符串,可选参数
 */
stompClient.send(destinationUrl, headers, body);
1、JSON支持

body消息可以使用JSON数据,使用JSON.stringify转换即可

stompClient.send("/app/hello",{},
        JSON.stringify({'name':$("#name").val(),'content':$("#content").val()}));
2、事务支持

STOMP 客户端支持在发送消息时用事务进行处理

// 该方法返回一个包含了事务 id、commit()、abort()的JavaScript 对象
var tx = stompClient.begin();
// 在headers对象中加入事务id,若没有添加,则会直接发送消息,不会以事务进行处理
stompClient.send("/app/hello",{transaction: tx.id},
        JSON.stringify({'name':$("#name").val(),'content':$("#content").val()}));
// 提交事务
tx.commit();
五、订阅和接收消息(含取消订阅和接收消息)
1、订阅和接收
/**
 * destinationUrl:服务端@SendTo匹配的URL
 * callback:为每次收到服务器推送的消息时的回调方法,该方法包含参数message(即收到的消息)
 * headers:附加的headers,JavaScript对象,可选参数
 * headers方法返回一个包含了id属性的JavaScript对象,可作为unsubscribe()方法的参数
 */
var subscription = varstompClient.subscribe(destinationUrl, callback, headers);
2、取消订阅和接收
subscription.unsubscribe();
六、了解更多

了解更多,可以百度,或是参考STOMP 客户端 API 整理

消息单发 - 点对点模式

为了更能表现出点对点的用户概念,所以这里引入SpringSecurity,然后通过内存里的账号登录。

一、引入SpringSecurity依赖并配置
1、引入依赖
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2、配置文件
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.inMemoryAuthentication()
                .withUser("lcy")
                .password("$2a$10$XcigeMfToGQ2bqRToFtUi.sG1V.HhrJV6RBjji1yncXReSNNIPl1K")  // 123
                .roles("admin")
                .and()
                .withUser("jyqc")
                .password("$2a$10$XcigeMfToGQ2bqRToFtUi.sG1V.HhrJV6RBjji1yncXReSNNIPl1K")
                .roles("user")
                .and()
                .withUser("xbyx")
                .password("$2a$10$XcigeMfToGQ2bqRToFtUi.sG1V.HhrJV6RBjji1yncXReSNNIPl1K")
                .roles("admin");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()   // 登录就可以访问
                .and()
                .formLogin().permitAll();       // 登录相关url都可以访问
    }
}
二、修改WebSocket配置文件以及Controller
1、WebSocket配置文件
@Configuration
@EnableWebSocketMessageBroker    // 开启WebSocket消息代理
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/topic","/queue");
        registry.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/chat").withSockJS();
    }
}
2、Controller

新增如下代码

@Autowired
private SimpMessagingTemplate template;

@MessageMapping("/chat")
public void chat(Principal principal, Chat chat){
    String from = principal.getName();
    chat.setFrom(from);
    // convertAndSendToUser内部会做处理
    // 发送的最终路径是/user/用户名/queue/chat
    template.convertAndSendToUser(chat.getTo(),"/queue/chat",chat);
}

convertAndSendToUser源码查看,可以发现里面对url进行修改,this.destinationPrefix默认是/user
在这里插入图片描述

三、HTML与JS
1、html页面
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>WebSocket单聊</title>
</head>
<body>
<div id="chat">
    <div id="chatsContent">
    </div>
    <div>
        聊天内容:
        <input type="text" id="content" placeholder="请输入聊天内容"><br>
        目标用户:
        <input type="text" id="to" placeholder="请输入目标用户"><br>
        <button id="send" type="button">发送</button>
    </div>
</div>
<!-- JS引用区域 -->
<script src="/webjars/jquery/jquery.min.js"></script>
<script src="/webjars/sockjs-client/sockjs.min.js"></script>
<script src="/webjars/stomp-websocket/stomp.min.js"></script>
<script src="/js/chat.js"></script>
</body>
</html>
2、chat.js
// STOMP客户端
var stompClient = null;

function connect() {
    var socket = new SockJS("/chat");
    stompClient = Stomp.over(socket);
    stompClient.connect({},function (frame) {
        // 因为后端的convertAndSendToUser处理了url,所以也就是为什么相比之前的群里多了个/user的原因
        stompClient.subscribe('/user/queue/chat',function (chat) {
            showGreeting(JSON.parse(chat.body));
        });
    });
}

function sendMsg() {
    stompClient.send("/app/chat",{},JSON.stringify({'content':$("#content").val(),'to':$("#to").val()}));
}

function showGreeting(message) {
    $("#chatsContent")
        .append("<div>" + message.from + ":" + message.content + "</div>");
}

$(function () {
    connect();
    $("#send").click(function () {
        sendMsg();
        $("#content").val("");
    })
});
测试

开启三个不同的浏览器测试,点对点模式没有问题。
在这里插入图片描述在这里插入图片描述在这里插入图片描述

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值