Greetings |
---|
STOMP是简单(或流式)文本定向消息传递协议。
STOMP提供可互操作的有线格式,以便STOMP客户端可以与任何STOMP消息代理进行通信,从而在多种语言,平台和代理之间提供简单而广泛的消息传递互操作性。
org.springframework.boot
spring-boot-starter-websocket
org.springframework
spring-messaging
如果你的spring boot版本高于1.5的话,要在配置文件中添加以下设置,否则在启动时会报错,因为高于1.5的它里面已经定义了subProtocolWebSocketHandler这个bean,与我们后面要定义的冲突了。
spring.main.allow-bean-definition-overriding=true
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketStompConfig extends WebSocketMessageBrokerConfigurationSupport implements WebSocketMessageBrokerConfigurer {
@Override
public void configureWebSocketTransport(WebSocketTransportRegistration registry) {
super.configureWebSocketTransport(registry);
}
@Override
public boolean configureMessageConverters(List messageConverters) {
return super.configureMessageConverters(messageConverters);
}
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
super.configureClientInboundChannel(registration);
}
@Override
public void configureClientOutboundChannel(ChannelRegistration registration) {
super.configureClientOutboundChannel(registration);
}
@Override
public void addArgumentResolvers(List argumentResolvers) {
super.addArgumentResolvers(argumentResolvers);
}
@Override
public void addReturnValueHandlers(List returnValueHandlers) {
super.addReturnValueHandlers(returnValueHandlers);
}
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/stomp")
.setHandshakeHandler(new DefaultHandshakeHandler() {
@Override
protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map attributes) {
//握手成功后调用,可以在这里保存用户id
return new Principal(((ServletServerHttpRequest) request).getServletRequest().getParameter("name"));
}
})
.setAllowedOrigins("*") // 添加允许跨域访问
//添加socket拦截器,用于从请求中获取客户端标识参数
.addInterceptors(new MyHandShakeInterceptor()).withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
//客户端发送消息的请求前缀
registry.setApplicationDestinationPrefixes("/app");
//客户端订阅消息的请求前缀,topic一般用于广播推送,queue用于点对点推送
registry.enableSimpleBroker("/topic", "/queue");
//服务端通知客户端的前缀,可以不设置,默认为user
registry.setUserDestinationPrefix("/user");
/* 如果是用自己的消息中间件,则按照下面的去配置,删除上面的配置
* registry.enableStompBrokerRelay("/topic", "/queue")
.setRelayHost("rabbit.someotherserver")
.setRelayPort(62623)
.setClientLogin("marcopolo")
.setClientPasscode("letmein01");
registry.setApplicationDestinationPrefixes("/app", "/foo");
* */
}
@Bean
public WebSocketHandler subProtocolWebSocketHandler() {
return new CustomSubProtocolWebSocketHandler(clientInboundChannel(), clientOutboundChannel());
}
//定义一个自己的权限验证类
class FastPrincipal implements Principal {
private final String name;
public FastPrincipal(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
}
import com.company.industrial_park.Util.JwtToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;
import java.util.Map;
/**
* 拦截器
* */
@Component
public class MyHandShakeInterceptor implements HandshakeInterceptor {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Map attributes) throws Exception {
// System.out.println(this.getClass().getCanonicalName() + "握手前...");
return true;
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception ex) {
// System.out.println(this.getClass().getCanonicalName() + "握手成功后...");
}
}
自定义这个主要是用来监听连接的建立与关闭,可以用来判断用户的在线状态等等。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.messaging.SubProtocolWebSocketHandler;
public class CustomSubProtocolWebSocketHandler extends SubProtocolWebSocketHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomSubProtocolWebSocketHandler.class);
@Autowired
private SessionHandler sessionHandler;
public CustomSubProtocolWebSocketHandler(MessageChannel clientInboundChannel, SubscribableChannel clientOutboundChannel) {
super(clientInboundChannel, clientOutboundChannel);
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
LOGGER.info("New websocket connection was established");
sessionHandler.register(session);
super.afterConnectionEstablished(session);
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
LOGGER.info("New websocket connection:"+session.getPrincipal().getName()+" was closed");
sessionHandler.remove(session);
super.afterConnectionClosed(session,closeStatus);
}
}
在这里我们可以对用户的session进行管理
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.WebSocketSession;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@Service
public class SessionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(SessionHandler.class);
// private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
private final Map sessionMap = new ConcurrentHashMap<>();
public SessionHandler() {
}
public void register(WebSocketSession session) {
sessionMap.put(session.getPrincipal().getName(), session);
}
public void remove(WebSocketSession session) {
sessionMap.remove(session.getPrincipal().getName());
}
public boolean isOnline(String openid){
return sessionMap.get(openid)!=null;
}
}
在这里我实现了点对点的通信,接受到消息后会
package com.company.industrial_park.controller.chat;
import java.io.UnsupportedEncodingException;
import java.security.Principal;
import java.util.logging.Logger;
import com.mysql.cj.log.LogFactory;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;
@Controller
public class StompController {
@Autowired
SimpMessagingTemplate SMT;
@Autowired
SessionHandler sessionHandler;
@MessageMapping("/message")
public void subscription(String str, Principal principal) throws MessagingException, UnsupportedEncodingException {
JSONObject obj=new JSONObject(str);
String openid=obj.getString("name");
SMT.convertAndSendToUser(openid,"/topic/chat",
"消息:"+obj.getString("content")+" from "+principal.getName());//发送消息到指定用户
boolean online=sessionHandler.isOnline(openid);
System.out.println(openid+" "+online);
}
}
把下面的html文件放到resources文件夹里的public里
stomp
Greetings
因为我代码是默认字符串是json格式的,所以发送的数据要是json格式,首先连接,