You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaFX客户端无法正确连接Spring Boot STOMP WebSocket主题排查

问题排查与解决:Spring Boot STOMP WebSocket + JavaFX Tyrus客户端异常

问题现象

  • JavaFX客户端通过Tyrus连接Spring Boot STOMP WebSocket服务,连接成功但仅收到零散字符(如'o'、'h')
  • 手动发送的订阅消息无效,无法获取/events主题的正常消息
  • 连接外部测试WebSocket(wss://ws.blockchain.info/inv)时出现相同异常
  • 前端Angular使用现成库可正常连接服务

服务器端配置(Spring Boot)

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer {
    private MessageBrokerRegistry registry;
   
    @Override    
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/socket")                
                .setAllowedOrigins("*")
                .withSockJS();    
    }
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {        
        registry.setApplicationDestinationPrefixes("/news")
                .enableSimpleBroker("/events")        
        ;
    }
    public MessageBrokerRegistry getRegistry() {
        return this.registry;    
    }
}

客户端代码(JavaFX + Tyrus)

BasicClient核心代码

@ClientEndpoint(configurator = CustomClientEndpointConfigurator.class,
        decoders = MessageDecoder.class,
        encoders = MessageEncoder.class
)
public class BasicClient implements MessageHandler {
    // 省略部分代码
    @SuppressWarnings("unused")
    @OnOpen
    public void onOpen(Session session) throws IOException {
        this.session = session;
        String[] strings = new String[1];
        strings[0] = "\"SUBSCRIBE id:sub-34 destination:/events\"";
        session.getAsyncRemote().sendText(Arrays.toString(strings));
        // 省略部分代码
    }
    // 省略部分代码
}

GeneralController调用代码

public class GeneralController {
    private BasicClient basicClient;
    @FXML
    void initialize() throws IOException {
        try {
           basicClient = BasicClient.getInstance();
           basicClient.connect();
           // 省略监听逻辑
        } catch (Exception e) {
            indicatorConnect.setStyle("-fx-background-color: red;");
            e.printStackTrace();
        }
    }
}

手动构造的Socket路径

public static final String LOCALHOST_WEBSOCKET_CONNECT = "/socket/%s/%s/websocket";
public static String createURISocketEDMS() throws URISyntaxException {
    // 省略路径构造逻辑
    return String.format("ws://localhost:8080"+LOCALHOST_WEBSOCKET_CONNECT,username,valueStr); 
}

问题原因分析

  1. SockJS协议不兼容:服务器端开启了SockJS,但客户端用原生WebSocket直接连接,SockJS返回的框架字符(如'o'代表心跳、'h'代表握手响应)被当作普通消息接收,导致看到零散字符。
  2. STOMP消息格式错误:手动构造的订阅消息不符合STOMP规范,STOMP要求消息格式为COMMAND\nheader1:value1\nheader2:value2\n\nbody^@,而非简单的字符串拼接。
  3. Tyrus功能局限:Tyrus是原生WebSocket客户端,无法自动处理SockJS的封装逻辑,而Angular使用的STOMP库会自动完成SockJS握手和消息格式转换。

解决方法

方案一:服务器端关闭SockJS(纯STOWS模式)

修改WebSocketConfiguration的registerStompEndpoints方法,移除withSockJS():

@Override    
public void registerStompEndpoints(StompEndpointRegistry registry) {
    registry.addEndpoint("/socket")                
            .setAllowedOrigins("*");
}

客户端连接路径改为ws://localhost:8080/socket,无需构造复杂路径。

方案二:客户端替换为支持SockJS+STOMP的库

放弃Tyrus,使用专门的STOMP客户端库,比如Spring官方的STOMP Client:

StompClient stompClient = StompClient.create("ws://localhost:8080/socket");
stompClient.connectHeaders(new HttpHeaders())
        .subscribe(session -> {
            session.subscribe("/events", message -> {
                // 处理收到的消息
                System.out.println("Received: " + message.getPayloadAsString());
            });
        });

方案三:修复Tyrus客户端的消息格式与SockJS处理(不推荐)

如果坚持使用Tyrus:

  1. 修正STOMP订阅消息格式:
String stompSubscribe = "SUBSCRIBE\nid:sub-34\ndestination:/events\n\n\0";
session.getAsyncRemote().sendText(stompSubscribe);

注意消息结尾必须加\n\n\0(空行+NULL终止符)。
2. 解析SockJS框架消息:收到的消息会被SockJS包裹在数组中(如["STOMP_MESSAGE_CONTENT"]),需要先剥离外层数组格式,再解析内部的STOMP消息。

额外验证提示

连接wss://ws.blockchain.info/inv时,该服务是纯WebSocket协议而非STOMP,需按照其要求发送JSON格式的订阅命令(如{"op":"unconfirmed_sub"}),而非STOMP的SUBSCRIBE消息。

内容的提问来源于stack exchange,提问作者Daniel Vai

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 04:14:52