前面说到,使用websocket通讯,现在说说应用上的通讯,stomp - streaming / simple text oriented protocol. 流/简单 文本协议。应用方面,一般采用该种协议,是websocket协议的一个子协议,了解一下既可。
stomp协议,配置时注意一个地方,stomp协议使用的中继器(路由)或者叫消息中介,默认在configureMessageBroker方法中,registry.enableSimpleBroker是使用内存中介,不依赖第三方组件,registry.enableStompBrokerRelay是使用第三方中间件,需要事先下载安装中间件,如activeMQ/RabbitMQ.本例子中使用内存中继。
具体代码实现:
1. WebSocketMessageBrokerConfigurer配置类,继承AbstractWebSocketMessageBrokerConfigurer重写下面方法。
package com.websocket.stomp;
import java.util.List;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.simp.config.ChannelRegistration;
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;
import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration;
/**
* stomp websocket的子协议,stomp: simple/streaming text oriented message protocol. 简单/流 文本消息协议,
* 选择使用内存中级,还是使用activeMQ等中间件服务器
* @author tomZ
* @date 2016年11月3日
* @desc TODO
*/
@Configuration
@EnableWebSocketMessageBroker
public class MyWebSocketMessageBrokerConfig extends AbstractWebSocketMessageBrokerConfigurer {
/**
* 连接的端点,客户端建立连接时需要连接这里配置的端点
*/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
//为java stomp client提供链接
registry.addEndpoint("/client")
.setAllowedOrigins("*")
.setHandshakeHandler(new MyHandshakeHandler())
.addInterceptors(new MyHandshakeInterceptor());
//为js客户端提供链接
registry.addEndpoint("/hello")
.setAllowedOrigins("*")
.setHandshakeHandler(new MyHandshakeHandler())
.addInterceptors(new MyHandshakeInterceptor())
.withSockJS();
}
/**
* applicationDestinationPrefixes应用前缀,所有请求的消息将会路由到@MessageMapping的controller上,
* enableStompBrokerRelay是代理前缀,而返回的消息将会路由到代理上,所有订阅该代理的将收到响应的消息。
*
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app");
registry.setUserDestinationPrefix("/user");
registry.enableSimpleBroker("/topic", "/queue")
// registry.enableStompBrokerRelay("/topic", "/queue")
//下面这配置为默认配置,如有变动修改配置启用就可以了
// .setRelayHost("127.0.0.1") //activeMq服务器地址
// .setRelayPort(61613)//activemq 服务器服务端口
// .setClientLogin("guest") //登陆账户
// .setClientPasscode("guest") //
;
}
/**
* 消息传输参数配置
*/
@Override
public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
// super.configureWebSocketTransport(registration);
registration.setMessageSizeLimit(8192).setSendBufferSizeLimit(8192).setSendTimeLimit(10000);
}
/**
* 输入通道参数设置
*/
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
// super.configureClientInboundChannel(registration);
//线程信息
registration.taskExecutor().corePoolSize(4).maxPoolSize(8).keepAliveSeconds(60);
}
/**
* 输出通道参数配置
*/
@Override
public void configureClientOutboundChannel(ChannelRegistration registration) {
// super.configureClientOutboundChannel(registration);
//线程信息
registration.taskExecutor().corePoolSize(4).maxPoolSize(8);
}
@Override
public boolean configureMessageConverters(List messageConverters) {
// return super.configureMessageConverters(messageConverters);
return true;
}
}
package com.websocket.stomp;
import java.security.Principal;
import java.util.Map;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.DefaultHandshakeHandler;
/**
* stomp 处理器
* @author tomZ
* @date 2016年11月4日
* @desc TODO
*/
public class MyHandshakeHandler extends DefaultHandshakeHandler {
///该方法可以重写用来为用户 添加标识 返回principal
@Override
protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler,
Map attributes) {
// TODO Auto-generated method stub
return super.determineUser(request, wsHandler, attributes);
}
}
Interceptor类。拦截器
package com.websocket.stomp;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
/**
* stomp握手拦截器
* @author tomZ
* @date 2016年11月4日
* @desc TODO
*/
public class MyHandshakeInterceptor extends HttpSessionHandshakeInterceptor {
private static final Logger logger = LoggerFactory.getLogger( MyHandshakeInterceptor.class);
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Map attributes) throws Exception {
logger.info("===============before handshake=============");
return super.beforeHandshake(request, response, wsHandler, attributes);
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Exception ex) {
logger.info("===============after handshake=============");
super.afterHandshake(request, response, wsHandler, ex);
}
}
3. controller, @MessageMapping用法和@RequestMapping相似,@SendTo是将消息发到指定的映射路由uri上去,@Subscribe是订阅uri注解,此外还有@SendToUser/@DestinationVariable等注解,可以查一下。
package com.tom.jeesite.web.stomp;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.annotation.SubscribeMapping;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
*
* @author tom
* @version 2016-06-12
*/
@Controller
public class MyStompTestController {
/**
* @MessageMapping 是app路由地址,客户端请求将交由其处理,@SendTo是返回消息路由到指定地址,订阅该地址的将接收到消息
* @param incoming
* @return
*/
@MessageMapping("/hi")
@SendTo("/topic/hi")
public String handleHi(String incoming) {
System.out.println("receive message : " + incoming);
return "hello, " + incoming;
}
/**
* 订阅,当有客户端订阅该内容,会有一次性响应
* @return
*/
@SubscribeMapping("/subscribeme")
public String subscribeThing() {
System.out.println("subscribe message called.");
return "thank you subscribe my channel";
}
}
4. Jsp页面:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%
String path = request.getContextPath();
String basePath = request.getServerName() + ":" + request.getServerPort() + path + "/";
%>
<%-- --%>
<%-- --%>
stomp测试
webSocket-stomp测试程序
启动运行,效果如下:
浏览器:
服务器:
如果出现spring扫描不到@MessageMapping注解方法的情况,在AbstractWebSocketMessageBrokerConfigurer的实现方法上加上注解:@ComponentScan("controller所在的包名")