描述:

将websocket应用在实际的项目中,用于后台应用向浏览器推送消息。

架构:

传统的springmvc基于xml配置的(但是websocket配置是基于java config配置,可以穿插的配置),前端采用vue.js.

启动报错:

java.lang.IllegalArgumentException: Async support must be enabled on a servlet and for all filters involved in async request processing. This is done in Java code using the Servlet API or by adding "<async-supported>true</async-supported>" to servlet and filter declarations in web.xml. Also you must use a Servlet 3.0+ container‘

解决:

需要在你的web.xml中所有的servlet和filter中添加<async-supported>true</async-supported>

引入依赖:spring版本需要4.x

<dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-websocket</artifactId>
     <version>${spring.version}</version>
</dependency>

webscoket相关配置

WebSocketStompConfig.java 用于配置webscoket

package com.xdja.dsc.webscoket.config;

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;

/**
 * 
 * @ClassName: WebSocketStompConfig
 * @Description: webscoket配置
 * @author niugang
 * @date 2018年11月6日
 */
@Configuration
//启动websocket和基于代理的STOMP消息
@EnableWebSocketMessageBroker
public class WebSocketStompConfig extends AbstractWebSocketMessageBrokerConfigurer {

  @Override
  public void registerStompEndpoints(StompEndpointRegistry registry) {
	//将clientMessage注册为STOMP的一个端点
	//客户端在订阅或发布消息到目的路径前,要连接该端点
	//setAllowedOrigins允许所有域连接,否则浏览器可能报403错误
    registry.addEndpoint("/clientMessage").setAllowedOrigins("*").withSockJS();
    
  }

  @Override
  public void configureMessageBroker(MessageBrokerRegistry registry) {
	
    registry.enableSimpleBroker("/queue", "/topic");
    //后台应用接收浏览器消息端点前缀,这个将直接路由到@MessageMapping上
    registry.setApplicationDestinationPrefixes("/app");
  }
  
}

MessageService.java  后台向前端推送消息类

package com.xdja.dsc.webscoket.service;

import com.alibaba.fastjson.JSONObject;
import com.xdja.dsc.validation.exception.ServiceException;
import com.xdja.dsc.validation.result.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessageSendingOperations;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

/**
 *
 * @ClassName: MessageService
 * @Description: 向浏览器后台应用推送消息
 * @author niugang
 * @date 2018年11月6日
 */
@Service
@EnableScheduling
public class MessageService {

	@Autowired
	private SimpMessageSendingOperations messageTemplate;

	private Logger logger = LoggerFactory.getLogger(MessageService.class);

	/**
     *
     * @param info
	* @date 2018年11月6日
	* @throws NullPointerException 参数为null时
	*/
	public void sendMessage(Result info) {
		if (info == null) {
			throw new NullPointerException("info object not null");
		}
		JSONObject jsonObject = new JSONObject();
		jsonObject.put("code", info.getCode());
		jsonObject.put("message", info.getMessage());
		String resStr = jsonObject.toJSONString();
		logger.info("send message to browser,message content [{}]", resStr);
		messageTemplate.convertAndSend("/topic/notify", resStr);
	}

   /**
    *
    * 40秒讯轮模拟向浏览器推消息
    * @date 2018年11月6日
    * @throws
    */
	@Scheduled(cron = "0/40 * *  * * ? ")
	public void test() {
		this.sendMessage(new Result(ServiceException.Service.DUPLICATED));
	}

}

ReceiveClientController.java 接收前端发送的消息

package com.xdja.dsc.webscoket.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.stereotype.Controller;

/**
 * 
 * @ClassName: ReciveClientController
 * @Description: 测试浏览器向后台应用推送的消息
 * @author niugang
 * @date 2018年11月6日
 */
@Controller
public class ReceiveClientController {

	private Logger logger = LoggerFactory.getLogger(ReceiveClientController.class);
	/**
	 * 接收客户端发送的消息
	 * @param content    
	 * @date 2018年11月6日
	 * @throws
	 */
	@MessageMapping("receive")
	public void receiveMessage(String content){
		logger.info("browser send message content [{}]",content);
	}
}

前端vue

<script>
    import SockJS from 'sockjs-client';
    import Stomp from 'stompjs';
    export default {
        data(){
            return {
        
            }
        },
        methods:{
            //初始化
            initWebSocket(){
                this.connection();
              //模拟客户端向后台推送消息
            let self = this;
                // 断开重连机制,尝试发送消息,捕获异常发生时重连
                this.timer = setInterval(() => {
                    try {
                        self.stompClient.send("/app/receive",{},"test");
                    } catch (err) {
                        console.log("断线了: " + err);
                        self.connection();
                    }
                }, 5000);
            },
            //建立连接
            connection() {
                // 建立连接对象
                //连接服务端提供的通信接口,连接以后才可以订阅广播消息和个人消息
                let _that=this;
                //后台服务ip和port
                _that.socket = new SockJS('http://localhost:8083/dsc/clientMessage');
                // 获取STOMP子协议的客户端对象
                _that.stompClient = Stomp.over(_that.socket);
                // 向服务器发起websocket连接
                _that.stompClient.connect('guest','guest',(frame) => {
                    // 订阅服务端提供的某个topic
                    _that.stompClient.subscribe('/topic/notify', (msg) => {
                        //msg.body存放的是服务端发送给我们的信息
                        let  resData= JSON.parse(msg.body);
                        _that.$notify({
                            title: '警告',
                            message: "病毒查杀后台消息推送测试......",
                            type: 'warning'
                        });
                    });
                }, (err) => {
                });

            },
            disconnect() {
                if (this.stompClient != null) {
                    this.stompClient.disconnect();
                    console.log("Disconnected");
                }
            }
        },
        //销毁页面之前,断开连接
        beforeDestroy: function () {
            //页面离开时断开连接,清除定时器
            this.disconnect();
            clearInterval(this.timer);
        },
        mounted(){
            //调用初始化websocket方法
            this.initWebSocket();
        },

    }
</script>

红框为浏览器向后台应用推送的消息

后台向浏览器推送的消息

                               

                                                                               微信公众号: 

                                               

                                                                             JAVA程序猿成长之路

                                            分享资源,记录程序猿成长点滴。专注于Java,Spring,SpringBoot,SpringCloud,分布式,微服务。

Logo

前往低代码交流专区

更多推荐