Spring WebSocket+STOMP客户端无法接收私信问题求助
前端客户端无法接收WebSocket服务器消息问题
前端客户端订阅聊天服务后无法接收服务器返回的消息,已尝试订阅/chat和/user/chat两个路径,均未解决。相关代码与日志如下:
JavaScript客户端代码
const stompClient = new StompJs.Client({ brokerURL: 'ws://localhost:8080/ws' }); stompClient.onConnect = (frame) => { //setConnected(true); console.log('Connected: ' + frame); //console.log('/user/queue/specific-user-user' + frame.headers['user-name']); stompClient.subscribe('/user/chat', msg => { console.log('response: ' + msg); }); }; stompClient.onWebSocketError = (error) => { console.error('Error with websocket', error); }; stompClient.onStompError = (frame) => { console.error('Broker reported error: ' + frame.headers['message']); console.error('Additional details: ' + frame.body); }; stompClient.activate();
Broker配置
@Configuration @EnableWebSocketMessageBroker public class WebSocketBrokerConfiguration implements WebSocketMessageBrokerConfigurer { @Autowired private HandshakeHandler handshakeHandler; @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws").setAllowedOrigins("http://localhost:4200").setHandshakeHandler(handshakeHandler); } @Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.enableSimpleBroker("/chat/"); registry.setUserDestinationPrefix("/user"); } }
事件监听器
@Component @Slf4j public class WebSocketEventListener { @Autowired private ChatManager chatManager; @EventListener private void handleSessionConnected(SessionConnectEvent event) { log.info("{}", event); } @EventListener private void handleSessionDisconnect(SessionDisconnectEvent event) { log.info("{}", event); } @EventListener private void handleSessionSubscribe(SessionSubscribeEvent event) { final Map<String, Object> nativeHeaders = (Map<String, Object>) event.getMessage().getHeaders().get("nativeHeaders"); final String sessionId = event.getMessage().getHeaders().get("simpSessionId").toString(); log.info("sessionId: {}", sessionId); log.info("subscribed destination: {}", nativeHeaders.get("destination")); log.info("subscribed user {}", event.getUser().getName()); SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE); headerAccessor.setSessionId(sessionId); log.info("Sending reply to: {}", event.getUser().getName()); Message message = new Message().setTo(event.getUser().getName()).setBody("hi"); simpMessagingTemplate.convertAndSendToUser( event.getUser().getName(), "/chat", message, headerAccessor.getMessageHeaders()); } }
握手处理器
@Configuration @Slf4j public class HandshakeHandler extends DefaultHandshakeHandler{ @Override protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) { Principal principal = request.getPrincipal(); if (principal == null) { principal = new AnonymousPrincipal(); String uniqueName = UUID.randomUUID().toString(); ((AnonymousPrincipal) principal).setName(uniqueName); log.info("new user: {}", uniqueName); } return principal; } }
TRACE级别日志(org.springframework.messaging)
...configuration.HandshakeHandler : new user: 8eee251d-2777-430c-b137-a5f462d7dd8f ....s.messaging.simp.stomp.StompDecoder : Decoded CONNECT {accept-version=[1.2,1.1,1.0], heart-beat=[10000,10000]} session=null ....s.m.s.b.SimpleBrokerMessageHandler : Processing CONNECT user=8eee251d-2777-430c-b137-...i.listener.WebSocketEventListener : SessionConnectEvent[GenericMessage [payload=byte[0], headers={simpMessageType=CONNECT, stompCommand=CONNECT, nativeHeaders={accept-version=[1.2,1.1,1.0], heart-beat=[10000,10000]}, simpSessionAttributes={}, simpHeartbeat=[J@7d5c7266, simpUser=AnonymousPrincipal(name=8eee251d-2777-430c-b137-a5f462d7dd8f), simpSessionId=d480e89a-813b-7776-23e0-6a0e34e9c70c}]] ...s.messaging.simp.stomp.StompEncoder : Encoding STOMP CONNECTED, headers={version=[1.2], heart-beat=[0,0], user-name=[8eee251d-2777-430c-b137-a5f462d7dd8f]} ...s.messaging.simp.stomp.StompDecoder : Decoded SUBSCRIBE {id=[sub-0], destination=[/user/chat]} session=null ..s.m.s.u.UserDestinationMessageHandler : Translated /user/chat -> [/chat-userd480e89a-813b-7776-23e0-6a0e34e9c70c] ...w.i.listener.WebSocketEventListener : sessionId: d480e89a-813b-7776-23e0-6a0e34e9c70c ...w.i.listener.WebSocketEventListener : subscribed destination: [/user/chat] ...w.i.listener.WebSocketEventListener : subscribed user 8eee251d-2777-430c-b137-a5f462d7dd8f ...websocket.chat.ChatManager : Sending reply to: 8eee251d-2777-430c-b137-a5f462d7dd8f ..m.s.u.UserDestinationMessageHandler : Translated /user/8eee251d-2777-430c-b137-a5f462d7dd8f/chat -> [/chat-userd480e89a-813b-7776-23e0-6a0e34e9c70c]
问题排查与解决方案
从日志和代码来看,核心问题出在消息代理的前缀配置以及用户目标路径的匹配上:
Broker前缀配置错误
当前enableSimpleBroker("/chat/")末尾带了斜杠,但服务器实际发送的目标路径是/chat-userxxx,并不包含/chat/前缀,导致消息无法被代理路由到客户端。
修正方法:将Broker配置中的前缀改为"/chat"(去掉末尾斜杠):@Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.enableSimpleBroker("/chat"); registry.setUserDestinationPrefix("/user"); }客户端订阅路径无需修改
客户端订阅/user/chat是正确的,Spring会自动将其转换为/chat-user<sessionId>,和服务器发送的目标路径完全匹配。消息序列化检查
确保Message类的序列化逻辑正常,避免因消息格式错误导致客户端无法解析。
修正后,服务器发送的消息会被正确路由到客户端的订阅路径,客户端即可接收到"hi"消息。
内容的提问来源于stack exchange,提问作者user3859651
相关产品推荐
相关产品推荐

