描述:
将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>

红框为浏览器向后台应用推送的消息
后台向浏览器推送的消息
微信公众号

