­

springboot整合websocket实现消息推送

  • 2019 年 12 月 7 日
  • 筆記

maven依赖

<?xml version="1.0" encoding="UTF-8"?>  <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  	<modelVersion>4.0.0</modelVersion>    	<groupId>net.dopan</groupId>  	<artifactId>websocket</artifactId>  	<version>0.0.1-SNAPSHOT</version>  	<packaging>jar</packaging>    	<name>websocket</name>  	<description>Demo project for Spring Boot</description>    	<parent>  		<groupId>org.springframework.boot</groupId>  		<artifactId>spring-boot-starter-parent</artifactId>  		<version>1.5.12.RELEASE</version>  		<relativePath/> <!-- lookup parent from repository -->  	</parent>    	<properties>  		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>  		<java.version>1.8</java.version>  	</properties>    	<dependencies>  		<dependency>  			<groupId>org.springframework.boot</groupId>  			<artifactId>spring-boot-starter-web</artifactId>  		</dependency>  <dependency>  			<groupId>org.mybatis.spring.boot</groupId>  			<artifactId>mybatis-spring-boot-starter</artifactId>  			<version>1.3.2</version>  		</dependency>    		<dependency>  			<groupId>org.springframework.boot</groupId>  			<artifactId>spring-boot-starter-test</artifactId>  			<scope>test</scope>  		</dependency>    		<dependency>  			<groupId>org.springframework.boot</groupId>  			<artifactId>spring-boot-starter-websocket</artifactId>  		</dependency>    		<dependency>  			<groupId>org.assertj</groupId>  			<artifactId>assertj-core</artifactId>  		</dependency>    	</dependencies>    	<build>  		<plugins>  			<plugin>  				<groupId>org.springframework.boot</groupId>  				<artifactId>spring-boot-maven-plugin</artifactId>  			</plugin>  		</plugins>  	</build>  </project>

常量类

package net.dopan.websocket.utils;    public class Constant {        //webSocket相关配置      //链接地址      public static String WEBSOCKETPATHPERFIX = "/ws-push";      public static String WEBSOCKETPATH = "/wsendpoint";      //消息代理路径      public static String WEBSOCKETBROADCASTPATH = "/topic";      //前端发送给服务端请求地址      public static final String FORETOSERVERPATH = "/welcome";      //服务端生产地址,客户端订阅此地址以接收服务端生产的消息      public static final String PRODUCERPATH = "/topic/getResponse";      //点对点消息推送地址前缀      public static final String P2PPUSHBASEPATH = "/user";      //点对点消息推送地址后缀,最后的地址为/user/用户识别码/msg      public static final String P2PPUSHPATH = "/msg";  }

controller

package net.dopan.websocket.controller;    import net.dopan.websocket.po.Message;  import net.dopan.websocket.po.Response;  import net.dopan.websocket.service.WebSocketService;  import net.dopan.websocket.utils.Constant;  import org.assertj.core.util.Lists;  import org.springframework.messaging.handler.annotation.MessageMapping;  import org.springframework.messaging.handler.annotation.SendTo;  import org.springframework.stereotype.Controller;    import javax.annotation.Resource;  import java.util.List;    @Controller  public class WsController {        @Resource      WebSocketService webSocketService;        // 测试地址 http://127.0.0.1:8080/index.html?userId=admin      @MessageMapping(Constant.FORETOSERVERPATH)//@MessageMapping和@RequestMapping功能类似,用于设置URL映射地址,浏览器向服务器发起请求,需要通过该地址。      @SendTo(Constant.PRODUCERPATH)//如果服务器接受到了消息,就会对订阅了@SendTo括号中的地址传送消息。      public Response say(Message message) throws Exception {          List<String> users = Lists.newArrayList();          users.add(message.getName());//此处只是为了方便测试,此值需要对应页面中订阅个人消息的userId          webSocketService.send2Users(users, new Response("hello " + message.getName()));            return new Response("Welcome, " + message.getName() + "!");      }  }

service

package net.dopan.websocket.service;    import net.dopan.websocket.po.Response;  import net.dopan.websocket.utils.Constant;  import org.springframework.beans.factory.annotation.Autowired;  import org.springframework.messaging.simp.SimpMessagingTemplate;  import org.springframework.stereotype.Service;    import java.util.List;    @Service  public class WebSocketService {        @Autowired      private SimpMessagingTemplate template;        /**       * 广播       * 发给所有在线用户       *       * @param msg       */      public void sendMsg(Response msg) {            template.convertAndSend(Constant.PRODUCERPATH, msg);      }        /**       * 发送给指定用户       *       * @param users       * @param msg       */      public void send2Users(List<String> users, Response msg) {          users.forEach(userName -> {              template.convertAndSendToUser(userName, Constant.P2PPUSHPATH, msg);          });      }  }

2个po类

package net.dopan.websocket.po;    public class Message {      private String name;        public String getName() {          return name;      }        public void setName(String name) {          this.name = name;      }  }
package net.dopan.websocket.po;    public class Response {      private String responseMessage;        public Response(String responseMessage){          this.responseMessage = responseMessage;      }        public String getResponseMessage() {          return responseMessage;      }        public void setResponseMessage(String responseMessage) {          this.responseMessage = responseMessage;      }  }

websocket类

package net.dopan.websocket;    import net.dopan.websocket.utils.Constant;  import org.springframework.context.annotation.Configuration;  import org.springframework.messaging.simp.config.MessageBrokerRegistry;  import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;  import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;  import org.springframework.web.socket.config.annotation.StompEndpointRegistry;    @Configuration  // @EnableWebSocketMessageBroker注解用于开启使用STOMP协议来传输基于代理(MessageBroker)的消息,这时候控制器(controller)  // 开始支持@MessageMapping,就像是使用@requestMapping一样。  @EnableWebSocketMessageBroker  public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {        @Override      public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {          //注册一个Stomp的节点(endpoint),并指定使用SockJS协议。          stompEndpointRegistry.addEndpoint(Constant.WEBSOCKETPATH).withSockJS();      }        @Override      public void configureMessageBroker(MessageBrokerRegistry registry) {          //服务端发送消息给客户端的域,多个用逗号隔开          registry.enableSimpleBroker(Constant.WEBSOCKETBROADCASTPATH, Constant.P2PPUSHBASEPATH);          //定义一对一推送的时候前缀          registry.setUserDestinationPrefix(Constant.P2PPUSHBASEPATH);          //定义websoket前缀          registry.setApplicationDestinationPrefixes(Constant.WEBSOCKETPATHPERFIX);      }  }

测试html

<!DOCTYPE html>  <html xmlns:th="http://www.thymeleaf.org">  <head>      <meta charset="UTF-8" />      <title>WebSocket</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://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>      <script th:inline="javascript">          var $_GET = (function(){              var url = window.document.location.href.toString();              var u = url.split("?");              if(typeof(u[1]) == "string"){                  u = u[1].split("&");                  var get = {};                  for(var i in u){                      var j = u[i].split("=");                      get[j[0]] = j[1];                  }                  return get;              } else {                  return {};              }          })();            var stompClient = null;          //此值有服务端传递给前端,实现方式没有要求          var userId = $_GET['userId'];            function setConnected(connected) {              document.getElementById('connect').disabled = connected;              document.getElementById('disconnect').disabled = !connected;              document.getElementById('conversationDiv').style.visibility = connected ? 'visible' : 'hidden';              $('#response').html();          }            function connect() {              var socket = new SockJS('/wsendpoint'); //1连接SockJS的endpoint是“wsendpoint”,与后台代码中注册的endpoint要一样。              stompClient = Stomp.over(socket);//2创建STOMP协议的webSocket客户端。              stompClient.connect({}, function(frame) {//3连接webSocket的服务端。                  setConnected(true);                  console.log('开始进行连接Connected: ' + frame);                  //4通过stompClient.subscribe()订阅服务器的目标是'/topic/getResponse'发送过来的地址,与@SendTo中的地址对应。                  stompClient.subscribe('/topic/getResponse', function(respnose){                      showResponse(JSON.parse(respnose.body).responseMessage);                  });                  //4通过stompClient.subscribe()订阅服务器的目标是'/user/' + userId + '/msg'接收一对一的推送消息,其中userId由服务端传递过来,用于表示唯一的用户,通过此值将消息精确推送给一个用户                  stompClient.subscribe('/user/' + userId + '/msg', function(respnose){                      console.log(respnose);                      showResponse1(JSON.parse(respnose.body).responseMessage);                  });              });          }              function disconnect() {              if (stompClient != null) {                  stompClient.disconnect();              }              setConnected(false);              console.log("Disconnected");          }            function sendName() {              var name = $('#name').val();              // 通过stompClient.send()向地址为"/welcome"的服务器地址发起请求,与@MessageMapping里的地址对应。              // 因为我们配置了registry.setApplicationDestinationPrefixes(Constant.WEBSOCKETPATHPERFIX);所以需要增加前缀/ws-push/              stompClient.send("/ws-push/welcome", {}, JSON.stringify({ 'name': name }));          }            function showResponse(message) {              var response = $("#response");              response.html(message);          }          function showResponse1(message) {              var response = $("#response1");              response.html(message);          }      </script>  </head>  <body onload="disconnect()">  <noscript><h2 style="color: #ff0000">貌似你的浏览器不支持websocket</h2></noscript>  <div>      <div>          <button id="connect" onclick="connect();">连接</button>          <button id="disconnect" disabled="disabled" onclick="disconnect();">断开连接</button>      </div>      <div id="conversationDiv">          <label>发送给对方的名字</label><input type="text" id="name" />          <button id="sendName" onclick="sendName();">发送</button>          <br />          广播<p id="response"></p>          <br />          私发<p id="response1"></p>      </div>  </div>    </body>  </html>