spring springboot2 结合 websocket+sockJs+stomp 实现个人订阅和广播模式

一  名词解释

  1.WebSocket

WebSocket 是发送和接收消息的底层API,WebSocket 协议提供了通过一个套接字实现全双工通信的功能。也能够实现 web 浏览器和 server 间的异步通信,全双工意味着 server 与浏览器间可以发送和接收消息。需要注意的是必须考虑浏览器是否支持

  2.SockJs

为了应对许多浏览器不支持WebSocket协议的问题,设计了备选SockJs。

SockJS 是 WebSocket 技术的一种模拟。SockJS 会 尽可能对应 WebSocket API,但如果 WebSocket 技术不可用的话,就会选择另外的通信方式协议。

  3.STOMP

SockJS 为 WebSocket 提供了 备选方案。但无论哪种场景,对于实际应用来说,这种通信形式层级过低。下面看一下如何 在 WebSocket 之上使用 STOMP协议,来为浏览器 和 server 间的 通信增加适当的消息语义。(STOMP—— Simple Text Oriented Message Protocol——面向消息的简单文本协议)

  4.三者之间的关系

WebSocket 是底层协议,SockJS 是WebSocket 的备选方案,也是 底层协议,而 STOMP 是基于 WebSocket(SockJS) 的上层协议

二 springboot的config

<!--  websocket-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

  1.基础配置

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/topic", "/queue", "/user"); //表示在topic和queue这两个域上服务端可以向客户端发消息
        registry.setApplicationDestinationPrefixes("/app");//客户端向服务器端发送时的主题上面需要加"/app"作为前缀
        registry.setUserDestinationPrefix("/user");//指定用户发送一对一的主题,前缀是"/user"
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/stomp/endpoint").withSockJS();
    }
}

第一步就是添加注解 此注解 这个配置类不仅配置了 WebSocket,还配置了基于代理的 STOMP 消息

第二步 配置一个消息代理..一个是允许服务器向客户端发送的域 一个是客户端想服务器发送的前缀,最后一个是指定一对第一发送的前缀

第三步 就是配置了节点,并且使用sockjs的方式

  2.监听连接与断开

  用到了redis管理session

    @Access(name = "监听websocket连接成功")
    @EventListener
    public void onConnectEvent(SessionConnectEvent event) {
        //获取Session连接信息
        StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage());
        //获取SessionId
        String sessionId = sha.getSessionId();
        //存储redis
        Map<String, String> map = new HashMap<String, String>();
        map.put(sessionId, agentNo);
        redisTemplate.opsForHash().putAll("webSocket", map);
    }

    @Access(name = "监听websocket断开连接")
    @EventListener
    public void onDisconnectEvent(SessionDisconnectEvent event) {
        //获取Session连接信息
        StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage());
        //获取SessionId
        String sessionId = sha.getSessionId();
        //redis删除此sessionId
        redisTemplate.opsForHash().delete("webSocket", sessionId);
    }
}

  三.实现前端代码

   1.引入js 

<!-- websocket   ****代表自己的项目路径  这个写自己的  -->
<script src="/***/sockjs-client/sockjs.min.js"></script>
<script src="/***/stomp-websocket/stomp.min.js"></script>
<!-- 参考 -->
<!-- 
https://cdn.bootcdn.net/ajax/libs/sockjs-client/1.4.0/sockjs.min.js
https://cdn.bootcdn.net/ajax/libs/stomp.js/2.3.3/stomp.min.js
-->

  2.连接和订阅消息

  使用了agentNo 区分订阅广播消息与个人消息

var stompClient = null;
var socket = null;
function connect() {
    //从sesionStorage获取坐席工号  下面连接头需要  这个按照自己需求修改
    agentNo = sessionStorage.getItem("agentNo");
    //建立连接对象(还未发起连接)  地址更换成自己的  连接服务器注册端点endPoint时,写访问服务器的全路径URL:
    socket = new SockJS('地址');
    //获取 STOMP 子协议的客户端对象
    stompClient = Stomp.over(socket);
    //建立连接,并且连接头上定义了坐席工号
    stompClient.connect({
        userName: agentNo    // <---这是头 在后面的监听  连接成功的类可以获取到  业务代码需要 按需删除
    }, function (frame) {
        console.log('Connected: ' + frame);
        //订阅广播消息
        stompClient.subscribe('/topic/greetings', function (greeting) {
            var data = JSON.parse(greeting.body).data;
            //实现自己的需求
        });
        //订阅个人消息   
        stompClient.subscribe('/user/' + agentNo + '/message', function (greeting) {
            var data = JSON.parse(greeting.body);
            //实现自己的需求
        });
    });
}

  2.发送消息

function send(data) {
    var param = {
        id: data,
        release: true
    }
    stompClient.send("/app/release", {}, JSON.stringify(param));  
 // <----- app 就是在config中定义的客户端往服务器发送的前缀,param是信息.必须用实体发送后台必须用实体接收
//release是后面controller中定义的 相当于RequestMapping中写的地址一样
}

  四.实现后端代码

    1.发送广播

    @Access(name = "发布公告")
    @MessageMapping("/release")
    @SendTo("/topic/greetings")
    public WebResult release(@RequestBody Announcement announcement) {
       //执行业务代码逻辑
    }

  2.发送个人消息

    @Autowired
    private SimpMessagingTemplate messagingTemplate;

     public void demo() {
     // messagingTemplate.convertAndSend(url,"");  //方法1
    //方法2  相当于/user/' + agentNo + '/message'    对应前端订阅的地址
    messagingTemplate.convertAndSendToUser(ledgerQuery.getAgentNo(), "/message", "");
}

  

 

 

posted @ 2020-09-01 10:46  Nuti  阅读(2116)  评论(1编辑  收藏  举报