当前位置: 首页 > 知识库问答 >
问题:

SpringWebSocket是否支持订阅id?

何修能
2023-03-14

STOMP规范规定订阅必须有id头。

https://stomp.github.io/stomp-specification-1.2.html#SUBSCRIBE_id_Header

订阅id标头

由于单个连接可以与服务器有多个打开的订阅,因此必须在框架中包含id标头以唯一标识订阅。id标头允许客户端和服务器将后续消息或取消订阅帧与原始订阅关联。在同一连接中,不同的订阅必须使用不同的订阅标识符。

然而,在spring的例子中https://spring.io/guides/gs/messaging-stomp-websocket/,它在订阅目标时不指定id。

function connect() {
    var socket = new SockJS('/gs-guide-websocket');
    stompClient = Stomp.over(socket);
    stompClient.connect({}, function (frame) {
        setConnected(true);
        console.log('Connected: ' + frame);
        stompClient.subscribe('/topic/greetings', function (greeting) {
            showGreeting(JSON.parse(greeting.body).content);
        });
    });
}

在Spring的API中,SimpMessageSendingOperations.convertAndSendToUser不明确支持id标头。

我的问题是如何在向客户端发送消息时指定id头?

共有1个答案

晏兴发
2023-03-14

我认为您不能使用订阅ID向特定客户端发送消息。Stomp定义了这个ID,Spring的实现在内部使用它为订阅目标地址的每个客户端创建消息。因此,订阅ID在跺脚通信中是透明的...您可以在客户端指定它,或者让Stomp JS(STOMP over WebSocket)创建一个唯一的。

如果您订阅前缀为/user/的目标,并使用org.springframework.messaging.simp.SimpMessagingTemboard#transtAndSendToUserorg. springframework. mail. simp.注解。SendToUser向单个客户端发送消息,Spring所做的是注册并基于原始目的地创建对基于自定义会话的目的地的订阅。换句话说,来自Spring的Javadoc:

当用户尝试订阅时,例如订阅“/user/queue/position updates”,将删除“/user”前缀,并根据会话id添加一个唯一的后缀,例如“/queue/position-updates-useri9oqdfzo”,以确保不同用户可以订阅相同的逻辑目标而不会发生冲突。

发送给用户时,例如“/user/{username}/queue/position updates”,删除“/user/{username}”前缀,并添加基于活动会话id的后缀,例如“/queue/position-updates-useri9oqdfzo”。

http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/messaging/simp/user/DefaultUserDestinationResolver.html

看见https://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html#websocket-跺脚用户目的地

编辑:

您不能使用订阅ID直接向其订阅的客户端发送消息,但可以使用客户端的会话ID。根据此处,您可以使用用户名向其发送消息。但是,您需要一个具有主体的经过身份验证的会话。或者,您可以在消息头中强制目标的会话ID,避免内部步骤来发现它,如下所示。

private void sendMessageToUser(String destinationSessionId, String message) {
    SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
    headerAccessor.setSessionId(destinationSessionId);
    headerAccessor.setLeaveMutable(true);
    messagingTemplate.convertAndSendToUser(destinationSessionId, "/subscribe/private", message, headerAccessor.getMessageHeaders());
}

这样做,如果会话中没有主体,我就无法使用@SendToUser注释。

 类似资料:
  • 我正在从iText 5移动到7。我们处理巨大的PDF文件,因此将整个PDF解析到内存中是不可取的。在5中,PdfReader上有一个特殊的构造函数,它强制执行“部分模式”。iText 7总是解析整个PDF还是总是有效地使用“部分模式”? 查看iText 7源代码,PdfReader似乎不再缓存文档内容。相反,PdfFile负责缓存。这意味着应该可以为每个页面创建一个新的Pdf文档,这将具有与Pdf

  • 一、STOMP协议介绍 STOMP即Simple (or Streaming) Text Orientated Messaging Protocol,简单(流)文本定向消息协议,它提供了一个可互操作的连接格式,允许STOMP客户端与任意STOMP消息代理(Broker)进行交互。STOMP协议由于设计简单,易于开发客户端,因此在多种语言和多种平台上得到广泛地应用。 STOMP协议的前身是TTMP协

  • 问题内容: 因此,我希望将其转换为这样的Java代码: JAXB是否可能? 曾经看到一些WebService Client存根生成器正在执行此操作,但也许不确定axis2 Webservice。 问题答案: 的 JAXB(JSR-222) 规范没有盖产生快速失败逻辑到域模型。现在,一种常见的做法是以注释(或XML)的形式表示验证规则,并对它们进行验证。 Bean验证(JSR-303) 对此进行了标

  • 问题内容: 我正在尝试执行一个简单的INSERT并返回标识(自动递增主键)。我试过了 我收到以下错误 SQLite是否支持SCOPE_IDENTITY? 如果可以,该如何使用? 如果不是,我(最好是“线程安全”)的替代方案是什么? 问题答案: 查看常见问题解答。该sqlite3_last_insert_rowid()函数将做到这一点。不过要小心触发器。

  • 我正在使用RDF4J工作台: 尽管我可以在github上的RDF4J存储库中看到对GeoSPARQL的引用,但目前似乎还没有实现。我在已清除的“带RDFS自旋支持的内存存储”存储库上运行了这个SPARQL更新查询,以在RDF4J工作台上设置测试: 这导致一个存储库具有一个事实。 现在,我尝试通过以下查询查找与文字多边形的重叠,该查询应该可以找到上面设置的一个事实: 此查询以HTML的形式给出了一个

  • 问题内容: 我想知道是否有任何方法可以在Java中实现。我认为,如果没有本地对闭包的支持,这是不可能的。 问题答案: Java 8(2014年3月18日发布)不支持curring。可以将Missingfaktor在答案中发布的示例Java代码重写为: …这是非常好的。就个人而言,有了Java 8,我几乎没有理由使用替代的JVM语言(例如Scala或Clojure)。当然,它们提供了其他语言功能,但