贱人直接删 发表于 2021-9-7 13:42:48

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

媒介



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

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


  一篇极简风,最最最基础的方式整合websocket :
《SpringBoot 整合WebSocket 简单实战案例》

地址: https://blog.csdn.net/qq_35387940/article/details/93483678
一篇极限风,最最最完善的方式整合websocket (stomp+rabbitmq处置惩罚负载):
 《Springboot 整合Websocket,Stomp协议,利用rabbitmq做消息代理,消息缓存》
 地址: https://blog.csdn.net/qq_35387940/article/details/108276136

但是按照最近看官们给我反应情况来看, 一个极简不符合需求,因为看官们虽然想简单,但是也想用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").withSockJS();

      //点对点
      registry.addEndpoint("/privateServer").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(&#39;Connected: &#39; + frame);
                stompClient.subscribe(&#39;/topic/all&#39;, function (response) {
                  var responseData = document.getElementById(&#39;responseData&#39;);
                  var p = document.createElement(&#39;p&#39;);
                  p.style.wordWrap = &#39;break-word&#39;;
                  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(&#39;content&#39;).value;
            stompClient.send("/all",JSON.stringify({&#39;content&#39;: content}));
      }
    </script>
</head>
<body onload="disconnect()">
<noscript><h2 style="color: #ff0000">Seems your browser doesn&#39;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(&#39;Connected: &#39; + frame);
                stompClient.subscribe(&#39;/user/&#39; + document.getElementById(&#39;user&#39;).value + &#39;/message&#39;, function (response) {
                  var responseData = document.getElementById(&#39;responseData&#39;);
                  var p = document.createElement(&#39;p&#39;);
                  p.style.wordWrap = &#39;break-word&#39;;
                  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: &#39;mylogin&#39;,
                passcode: &#39;mypasscode&#39;,
// additional header
                &#39;accessToken&#39;: &#39;HWPO325J9814GBHJF933&#39;
            };
            var content = document.getElementById(&#39;content&#39;).value;
            var to = document.getElementById(&#39;to&#39;).value;
            stompClient.send("/alone", {&#39;accessToken&#39;: &#39;HWPO325J9814GBHJF933&#39;}, JSON.stringify({
                &#39;content&#39;: content,
                &#39;to&#39;: to
            }));
      }
    </script>
</head>
<body onload="disconnect()">
<noscript><h2 style="color: #ff0000">Seems your browser doesn&#39;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负载问题 。













免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: Springboot 整合 WebSocket ,利用STOMP协议 ,前后端整合实战 (一)