Spring Boot接收StompJS心跳(无SockJS)及服务器发心跳方法
关于Spring Boot与StompJS WebSocket心跳的问题
一、Angular端已配置的心跳设置
我在Angular中通过StompJS设置了客户端心跳,代码为this.client.heartbeat.outgoing = 2000;,完整Angular代码如下:
this.client = new Client({ brokerURL: 'ws://localhost:8080/gs-guide-websocket', onConnect: () => { this.client.subscribe('/topic/greetings/' + this.username, (messages: { body }) => { // 处理消息 }); this.client.heartbeat.outgoing = 2000; } }); this.client.activate();
二、当前Spring Boot配置
我的Spring Boot WebSocket配置代码如下:
@Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/topic"); config.setApplicationDestinationPrefixes("/app"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/gs-guide-websocket").setAllowedOrigins("http://localhost:4200"); System.out.println("entered the websocket connection"); } }
三、疑问与需求
- 如何在Spring Boot中捕获客户端发送的心跳,以此定期了解哪些客户端处于在线连接状态?
- Spring Boot服务器能否主动向客户端发送心跳?如果可以,如何实现?
解决方案
1. 捕获客户端心跳与跟踪在线客户端
实际上,不需要直接捕获心跳包来跟踪在线状态,Spring WebSocket提供了更直接的会话事件监听方式:
方式一:监听连接/断开事件维护在线列表
通过监听Spring发布的SessionConnectEvent和SessionDisconnectEvent,可以在客户端连接、断开时更新在线客户端列表:
@Component public class WebSocketSessionListener { // 用线程安全的Map存储在线客户端(SessionID -> 用户名) private final Map<String, String> onlineClients = new ConcurrentHashMap<>(); @EventListener public void onClientConnect(SessionConnectEvent event) { StompHeaderAccessor accessor = StompHeaderAccessor.wrap(event.getMessage()); // 从连接头中获取客户端传递的用户名(Angular端需提前配置) String username = accessor.getNativeHeader("username").get(0); String sessionId = accessor.getSessionId(); onlineClients.put(sessionId, username); System.out.println("客户端上线:" + username + ",SessionID:" + sessionId); } @EventListener public void onClientDisconnect(SessionDisconnectEvent event) { StompHeaderAccessor accessor = StompHeaderAccessor.wrap(event.getMessage()); String sessionId = accessor.getSessionId(); String username = onlineClients.remove(sessionId); System.out.println("客户端下线:" + username + ",SessionID:" + sessionId); } }
对应Angular端需要在连接时传递用户名:
this.client = new Client({ brokerURL: 'ws://localhost:8080/gs-guide-websocket', connectHeaders: { username: this.username // 向服务端传递用户名 }, onConnect: () => { this.client.heartbeat.outgoing = 2000; this.client.subscribe('/topic/greetings/' + this.username, (messages: { body }) => { // 处理消息 }); } });
方式二:直接拦截心跳帧
如果需要主动捕获心跳包,可以自定义ChannelInterceptor拦截所有入站消息,判断是否为心跳帧:
@Component public class HeartbeatInterceptor implements ChannelInterceptor { @Override public Message<?> preSend(Message<?> message, MessageChannel channel) { StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); if (accessor != null && StompCommand.SEND.equals(accessor.getCommand())) { // 心跳帧通常是空内容,且无目标地址 byte[] payload = (byte[]) message.getPayload(); if (payload.length == 0 && accessor.getDestination() == null) { String sessionId = accessor.getSessionId(); System.out.println("收到客户端心跳:" + sessionId); // 可在此更新客户端最后活跃时间 } } return message; } }
然后在WebSocket配置中注册拦截器:
@Override public void configureClientInboundChannel(ChannelRegistration registration) { registration.interceptors(new HeartbeatInterceptor()); }
2. 服务器主动向客户端发送心跳
Spring WebSocket默认支持服务器端心跳,只需在配置中设置心跳间隔即可:
@Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/topic") // 数组第一个值是服务器发送心跳的间隔(毫秒),第二个是期望收到客户端心跳的间隔 .setHeartbeatValue(new long[]{10000, 10000}); config.setApplicationDestinationPrefixes("/app"); }
同时Angular端需配置对应的心跳接收间隔,确保两端匹配:
this.client = new Client({ brokerURL: 'ws://localhost:8080/gs-guide-websocket', heartbeat: { outgoing: 2000, // 客户端每2秒发心跳 incoming: 10000 // 客户端期望每10秒收到服务器心跳 }, connectHeaders: { username: this.username }, onConnect: () => { this.client.subscribe('/topic/greetings/' + this.username, (messages: { body }) => { // 处理消息 }); } });
内容的提问来源于stack exchange,提问作者UltraLegend
相关产品推荐
相关产品推荐

