Springboot 整合 WebSocket ,使用STOMP协议 ,前后端整合实战 (一)

前言

websocket ,对于我来说已经是老朋友了。 

 很久很久以前,我写过两篇websocket 相关的文章。



  一篇极简风,最最最基础的方式整合websocket :

《SpringBoot 整合WebSocket 简单实战案例》

地址: SpringBoot 整合WebSocket 简单实战案例_默默不代表沉默-CSDN博客_springboot websocket

一篇极限风,最最最完善的方式整合websocket (stomp+rabbitmq处理负载):

 《Springboot 整合Websocket,Stomp协议,使用rabbitmq做消息代理,消息缓存

 地址: Springboot 整合Websocket+Stomp协议+RabbitMQ做消息代理 实例教程_默默不代表沉默-CSDN博客_rabbitmq stomp协议

但是按照最近看官们给我反应情况来看, 一个极简不符合需求,因为看官们虽然想简单,但是也想用stomp。

然而那个极限的呢,又太复杂,看官们不乐意去整合rabbitmq。

那么,这篇文章的意义就出来了。
 

正文

本篇内容:


1.后端整合websocket (STOMP协议)

2.群发、指定单发

3.前端简单页面示例(接收、发送消息)

事不宜迟,开始敲代码。

先看下这次实战案例项目结构:


1. pom.xml 核心依赖的导入:
 

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

2.application.yml  (我就单纯设置一下端口)
 

server:
  port: 9908

3.WebSocketConfig.java

import org.springframework.context.annotation.Configuration;
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;


/**
 * @Author JCccc
 * @Description   EnableWebSocketMessageBroker-注解开启STOMP协议来传输基于代理的消息,此时控制器支持使用@MessageMapping
 * @Date 2021/6/30 8:53
 */

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        //topic用来广播,user用来实现点对点
        config.enableSimpleBroker("/topic", "/user");
    }

    /**
     * 开放节点
     * @param registry
     */
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        //注册两个STOMP的endpoint,分别用于广播和点对点

 
        //广播
        registry.addEndpoint("/publicServer").setAllowedOrigins("*").withSockJS();

        //点对点
        registry.addEndpoint("/privateServer").setAllowedOrigins("*").withSockJS();
    }


}

4.推送消息的实体类 Message.java

/**
 * @Author JCccc
 * @Description
 * @Date 2021/8/20 9:26
 */
public class Message {

    /**
     * 消息编码
     */
    private String code;

    /**
     * 来自(保证唯一)
     */
    private String form;

    /**
     * 去自(保证唯一)
     */
    private String to;

    /**
     * 内容
     */
    private String content;

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getForm() {
        return form;
    }

    public void setForm(String form) {
        this.form = form;
    }

    public String getTo() {
        return to;
    }

    public void setTo(String to) {
        this.to = to;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

5.前端简单调试页面 

① publicExample.html 监听广播消息的测试页面

<html>
<head>
    <meta charset="UTF-8">
    <title>等系统推消息</title>
    <script src="https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js"></script>
    <script src="https://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js"></script>
    <script src="https://code.jquery.com/jquery-3.2.0.min.js"
            integrity="sha256-JAW99MJVpJBGcbzEuXk4Az05s/XyDdBomFqNlM3ic+I=" crossorigin="anonymous"></script>

    <script type="text/javascript">
        var stompClient = null;

        function setConnected(connected) {
            document.getElementById("connect").disabled = connected;
            document.getElementById("disconnect").disabled = !connected;
            $("#response").html();
        }

        function connect() {

             var socket = new SockJS("http://localhost:9908/publicServer");
            stompClient = Stomp.over(socket);
            stompClient.connect({}, function (frame) {
                setConnected(true);
                console.log('Connected: ' + frame);
                stompClient.subscribe('/topic/all', function (response) {
                    var responseData = document.getElementById('responseData');
                    var p = document.createElement('p');
                    p.style.wordWrap = 'break-word';
                    p.appendChild(document.createTextNode(response.body));
                    responseData.appendChild(p);
                });
            },{});
        }

        function disconnect() {
            if (stompClient != null) {
                stompClient.disconnect();
            }
            setConnected(false);
            console.log("Disconnected");
        }

        function sendMsg() {
            var content = document.getElementById('content').value;
            stompClient.send("/all",{},JSON.stringify({'content': content}));
        }
    </script>
</head>
<body onload="disconnect()">
<noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websocket relies on Javascript being
    enabled. Please enable
    Javascript and reload this page!</h2></noscript>
<div>
    <div>
        <labal>连接广播频道</labal>
        <button id="connect" onclick="connect();">Connect</button>
        <labal>取消连接</labal>
        <button id="disconnect" disabled="disabled" onclick="disconnect();">Disconnect</button>
    </div>
    <div id="conversationDiv">
        <labal>广播消息</labal>
        <input type="text" id="content"/>
        <button id="sendMsg" onclick="sendMsg();">Send</button>

    </div>
    <div>
        <labal>接收到的消息:</labal>
        <p id="responseData"></p>

    </div>

</div>

</body>
</html>

简析: 

趁热打铁,我们模拟系统后端给前端推送广播消息,通过接口模拟:
 

TestController.java

/**
 * @Author JCccc
 * @Description
 * @Date 2021/8/20 8:53
 */
@Controller
public class TestController {
    @Autowired
    public SimpMessagingTemplate template;

    /**
     * 广播
     *
     * @param msg
     */
    @ResponseBody
    @RequestMapping("/pushToAll")
    public void subscribe( @RequestBody Message msg) {
        template.convertAndSend("/topic/all", msg.getContent());
    }
}

简析:


我们推送消息,直接用 SimpMessagingTemplate ,

用的是convertAndSend 广播方式推送到对于的主题目的地 destination 。

(可以看到其实还有convertAndSendToUser ,不着急,后面会说,这是发送给某个连接用户的)

 直接把项目跑起来,打开页面开始测试:

 我们先点击connect ,连接成功:

可以看到实际上stomp.min.js 最终也是转化成为 ws/wss这种方式成功连接:

调用测试接口,推送广播消息:

 

 在console其实也能看到:

 广播功能就到这,接下来是 点对点。

前端页面:

privateExample.html

<html>
<head>
    <meta charset="UTF-8">
    <title>聊起来</title>
    <script src="https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js"></script>
    <script src="https://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js"></script>
    <script src="https://code.jquery.com/jquery-3.2.0.min.js"
            integrity="sha256-JAW99MJVpJBGcbzEuXk4Az05s/XyDdBomFqNlM3ic+I=" crossorigin="anonymous"></script>

    <script type="text/javascript">
        var stompClient = null;

        function setConnected(connected) {
            document.getElementById("connect").disabled = connected;
            document.getElementById("disconnect").disabled = !connected;
            $("#response").html();
        }

        function connect() {
            var socket = new SockJS("http://localhost:9908/privateServer");
            stompClient = Stomp.over(socket);
            stompClient.heartbeat.outgoing = 20000;
            // client will send heartbeats every 20000ms
            stompClient.heartbeat.incoming = 0;
            stompClient.connect({}, function (frame) {
                setConnected(true);
                console.log('Connected: ' + frame);
                stompClient.subscribe('/user/' + document.getElementById('user').value + '/message', function (response) {
                    var responseData = document.getElementById('responseData');
                    var p = document.createElement('p');
                    p.style.wordWrap = 'break-word';
                    p.appendChild(document.createTextNode(response.body));
                    responseData.appendChild(p);
                });


            });

        }

        function disconnect() {
            if (stompClient != null) {
                stompClient.disconnect();
            }
            setConnected(false);
            console.log("Disconnected");

        }

        function sendMsg() {
            var headers = {
                login: 'mylogin',
                passcode: 'mypasscode',
// additional header
                'accessToken': 'HWPO325J9814GBHJF933'
            };
            var content = document.getElementById('content').value;
            var to = document.getElementById('to').value;
            stompClient.send("/alone", {'accessToken': 'HWPO325J9814GBHJF933'}, JSON.stringify({
                'content': content,
                'to': to
            }));
        }
    </script>
</head>
<body onload="disconnect()">
<noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websocket relies on Javascript being
    enabled. Please enable
    Javascript and reload this page!</h2></noscript>
<div>
    <div>
        <labal>连接用户</labal>
        <input type="text" id="user"/>
        <button id="connect" onclick="connect();">Connect</button>

    </div>

    <div>
        <labal>取消连接</labal>
        <button id="disconnect" disabled="disabled" onclick="disconnect();">Disconnect</button>
    </div>

    <div id="conversationDiv">
        <labal>发送消息</labal>
        <div>
            <labal>内容</labal>
            <input type="text" id="content"/>
        </div>
        <div>
            <labal>发给谁</labal>
            <input type="text" id="to"/>
        </div>
        <button id="sendMsg" onclick="sendMsg();">Send</button>

    </div>
    <div>
        <labal>接收到的消息:</labal>
        <p id="responseData"></p>

    </div>
</div>

</body>
</html>

简析:


趁热打铁,我们模拟系统后端给前端推送点对点消息(指定某个用户),通过接口模拟:

    /**
     * 点对点
     */
    @ResponseBody
    @RequestMapping("/pushToOne")
    public void queue(@RequestBody Message msg) {
        System.out.println("进入方法");
        /*使用convertAndSendToUser方法,第一个参数为用户id,此时js中的订阅地址为
        "/user/" + 用户Id + "/message",其中"/user"是固定的*/
        template.convertAndSendToUser(msg.getTo(), "/message", msg.getContent());

    }

用的是convertAndSendToUser 推送到指定的用户 ,对于的主题目的地 destination(/message) 

也许看到这里,你会觉得很奇怪,为什么我们推的主题是 /message,但是前端订阅的却是
 

"/user/" + 用户Id + "/message"

我们直接看源码:

 

 ok,应该不用多说,代码帮我们自己拼接起来了,跟前端订阅规则保持一致。

直接跑项目,测试一下:


模拟我们连接的用户标识 19901 ,连接成功

使用postman调用我们的测试接口,模拟系统指定推送消息到 19901 这个人 :

然后也给20011发送一下消息:

 然后看到对应的用户也能收到对应的消息:

对点推送,广播推送,也已经完毕了 。

这种情况就是相当于使用http接口方式,去撮合后端服务做 消息推送。

其实spring还提供了一些好玩的注解,

@MessageMapping  这个注解是对称于  @EnableWebSocketMessageBroker

也就是说,如果我们使用@EnableWebSocketMessageBroker ,那么我们在接口上面其实就能直接使用  @MessageMapping。

怎么用?

然后前端代码里面 的使用:

 

 这样我们就可以通过前端直接调用相关接口了:

个人认为其实没有必要使用这个注解,直接通过前端调用后端服务代码,我们服务端来根据Message里面 的 发送方、接收方、消息类型(点对点、广播)就可以直接完成相关也业务场景了。

ok,该篇就到此。

下一篇,给大家带来怎么解决websocket负载问题 。

  • 43
    点赞
  • 144
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 27
    评论
Spring Boot提供了一个非常方便的方式来整合WebSocketSTOMP协议,可以非常容易地在应用程序中添加实时消息推送功能。下面是实现的步骤: 1.添加依赖 在pom.xml中添加以下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> ``` 2.创建WebSocket配置类 创建一个类来配置WebSocket支持: ```java @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.enableSimpleBroker("/topic"); registry.setApplicationDestinationPrefixes("/app"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws").setAllowedOrigins("*").withSockJS(); } } ``` 该类通过@EnableWebSocketMessageBroker注解启用了WebSocket消息代理功能,并实现了WebSocketMessageBrokerConfigurer接口来配置消息代理。 configureMessageBroker()方法配置了一个简单的消息代理,它将以“/topic”为前缀的消息发送到代理。应用程序的目标前缀将是“/app”。 registerStompEndpoints()方法将“/ws”路径注册为STOMP端点,并启用SockJS支持。 3.编写控制器 创建一个控制器来处理WebSocket请求: ```java @Controller public class WebSocketController { @MessageMapping("/hello") @SendTo("/topic/greetings") public Greeting greeting(HelloMessage message) throws Exception { Thread.sleep(1000); // simulated delay return new Greeting("Hello, " + message.getName() + "!"); } } ``` @MessageMapping注解表示该方法可以处理来自“/app/hello”的消息。@SendTo注解表示当处理完成后,将结果发送到“/topic/greetings”主题。 4.创建实体类 创建HelloMessage和Greeting实体类: ```java public class HelloMessage { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } public class Greeting { private String content; public Greeting(String content) { this.content = content; } public String getContent() { return content; } } ``` 5.创建前端页面 在前端页面中使用STOMP.js和SockJS来连接WebSocket,发送和接收消息: ```html <!DOCTYPE html> <html> <head> <title>WebSocket Example</title> <script src="https://cdn.jsdelivr.net/sockjs/1.1.4/sockjs.min.js"></script> <script src="https://cdn.jsdelivr.net/stomp.js/2.3.3/stomp.min.js"></script> <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script> </head> <body> <div> <label for="name">What is your name?</label> <input type="text" id="name" name="name"> <button id="connect">Connect</button> </div> <br/> <div> <label for="message">Message:</label> <input type="text" id="message" name="message"> <button id="send">Send</button> </div> <br/> <div id="greetings"></div> <script> var stompClient = null; function connect() { var socket = new SockJS('/ws'); stompClient = Stomp.over(socket); stompClient.connect({}, function(frame) { console.log('Connected: ' + frame); stompClient.subscribe('/topic/greetings', function(greeting){ showGreeting(JSON.parse(greeting.body).content); }); }); } function disconnect() { if (stompClient !== null) { stompClient.disconnect(); } console.log("Disconnected"); } function sendName() { stompClient.send("/app/hello", {}, JSON.stringify({'name': $("#name").val()})); } function showGreeting(message) { $("#greetings").append("<tr><td>" + message + "</td></tr>"); } $(function () { $("form").on('submit', function (e) { e.preventDefault(); }); $("#connect").click(function() { connect(); }); $("#disconnect").click(function() { disconnect(); }); $("#send").click(function() { sendName(); }); }); </script> </body> </html> ``` 在页面中,我们使用了SockJS和STOMP.js,创建一个WebSocket连接。我们可以使用connect()函数来建立连接,使用sendName()函数来发送消息,并使用showGreeting()函数来显示接收到的消息。 最后,我们需要在应用程序的主类上添加@SpringBootApplication注解,并运行应用程序。 这样,当用户在页面上输入一个名字并点击“Connect”按钮时,将建立一个WebSocket连接,并向服务器发送一个消息。服务器将在1秒钟后返回一个问候语,并将其发送到“/topic/greetings”主题。浏览器将接收到这个消息,并通过showGreeting()函数显示它。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小目标青年

对你有帮助的话,谢谢你的打赏。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值