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

Qt客户端无法接收Spring Boot STOMP订阅消息排查求助

问题:Spring Boot STOMP服务器与Qt客户端通信,客户端收不到广播消息

服务器端实现

@Controller
class GreetingController {

     @MessageMapping("/hello")
     @SendTo("/topic/greetings")
     @Throws(Exception::class)
     fun greeting(@RequestBody message : HelloMessage) : Greeting  {
         Thread.sleep(1000); // simulated delay
         return Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!")
     }
}

Qt客户端关键代码

StompClient发送帧方法

bool StompClient::sendFrame(std::shared_ptr<StompFrame> stompFrame) {
     if (!stompFrame->isBinaryBody()) {
         QString str = stompFrame->convertToQString();
         m_Socket.sendTextMessage(str);
         qDebug() << "Send stomp frame " << str;
         return true;
    }

    QByteArray ba = stompFrame->convertToByteArray();
    m_Socket.sendBinaryMessage(ba);
    return true;
 }

StompFrameCreator创建订阅/发送帧方法

std::shared_ptr<StompFrame> StompFrameCreator::createSubscribeFrame(const QString&   destination) {
     auto stompFrame = std::make_shared<StompFrame>();
     stompFrame->setCommand(StompFrame::HeaderTypes::SUBSCRIBE);
     stompFrame->addHeader("destination", destination);
     return stompFrame;
}

std::shared_ptr<StompFrame> StompFrameCreator::createSendTextFrame(const QString& destination, const QString& message) {
     auto stompFrame = std::make_shared<StompFrame>();
     stompFrame->setCommand(StompFrame::HeaderTypes::SEND);
     stompFrame->addHeader("destination", destination);
     stompFrame->addHeader("content-type", "application/json");
     stompFrame->addTextBody(message);
     return stompFrame;
 }

StompDemoView业务逻辑方法

void StompDemoView::sendGreeting() {
     qDebug() << "Send greeting";
     StompFrameCreator stompFrameCreator;

     QJsonObject jsonObject;
     jsonObject.insert("name", QJsonValue::fromVariant(m_NameLineEdit->text()));
     QJsonDocument doc(jsonObject);

     auto publishFrame = stompFrameCreator.createSendTextFrame("/app/hello", doc.toJson());
     qDebug() << "Sending " << doc.toJson();
     m_StompClient.sendFrame(publishFrame);
 }

void StompDemoView::clientConnected() {
    StompFrameCreator stompFrameCreator;
    qDebug() << "Subscribe to topic";
    auto subscribeFrame = stompFrameCreator.createSubscribeFrame(0, "/topic/greetings", "auto");
    m_StompClient.sendFrame(subscribeFrame);
}

已确认的服务器日志流程

订阅请求接收日志

Decoded SUBSCRIBE {id=[0], destination=[/topic/greetings], ack=[auto]} session=null
From client: SUBSCRIBE /topic/greetings id=0 session=6cc3a2b6-48a5-1c1c-2366-d746224d60e8
Processing SUBSCRIBE /topic/greetings id=0 session=6cc3a2b6-48a5-1c1c-2366-d746224d60e8

消息处理与广播日志

Decoded SEND {destination=[/app/hello], content-type=[application/json]} session=null         
application/json payload={
"name": "Cris"
}

From client: SEND /app/hello session=6cc3a2b6-48a5-1c1c-2366-d746224d60e8 application/json payload={
 "name": "Cris"
}

Searching methods to handle SEND /app/hello session=6cc3a2b6-48a5-1c1c-2366-d746224d60e8 application/json payload={
"name": "Cris"
 }
 , lookupDestination='/hello'
Found 1 handler methods: [{[MESSAGE],[/hello]}]
Invoking GreetingController#greeting[1 args]
Arguments: [HelloMessage@3903b632]
Processing return value with SendToMethodReturnValueHandler [annotationRequired=true]
Processing MESSAGE destination=/topic/greetings session=6cc3a2b6-48a5-1c1c-2366-d746224d60e8 payload={"content":"Hello, Cris!"}
Broadcasting to 1 sessions.

排查方向

  • 验证STOMP帧格式合规性

    • 检查客户端发送的订阅帧是否严格遵循STOMP规范:帧结尾必须是\n\n\0(换行+空行+空字节),确保convertToQString/convertToByteArray方法正确生成该结束符
    • 通过抓包工具查看WebSocket传输的原始数据,确认服务器广播的MESSAGE帧格式是否正确
  • 检查WebSocket连接与信号槽

    • 在errorOccurred槽函数中打印具体错误信息,排除连接异常
    • 确认信号槽连接成功:打印connect调用的返回值,或在textMessageReceived/binaryMessageReceived槽中添加日志,验证是否有数据触发信号
    • 持续检查QWebSocket的状态是否为QAbstractSocket::ConnectedState,避免连接意外断开
  • 核对STOMP会话与订阅信息

    • 确认服务器广播时的session ID与客户端当前会话ID一致,避免会话不匹配导致消息无法推送
    • 检查订阅的destination是否完全匹配:/topic/greetings的拼写、大小写、路径层级必须和服务器广播目标一致
  • 调试客户端STOMP解析逻辑

    • 临时在WebSocket消息槽中直接打印原始收到的内容,确认是否有数据到达客户端
    • 检查StompFrame的解析代码是否能正确处理服务器发送的MESSAGE帧,比如是否正确提取headers和payload,是否遗漏帧结束符导致解析失败
  • 验证服务器广播配置

    • 确认Spring Boot的STOMP配置中,/topic/前缀已正确配置广播代理(如@EnableWebSocketMessageBroker中启用simpleBroker("/topic/")),确保广播消息能正确路由到订阅客户端

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 20:37:48