【问题标题】:Does spring-websocket STOMP support SUBSCRIBE id?spring-websocket STOMP 是否支持 SUBSCRIBE id?
【发布时间】:2017-12-05 01:41:31
【问题描述】:

STOMP 规范规定 SUBSCRIBE 必须 具有 id 标头

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

订阅 id 标头

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

但是,在 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 标头?

【问题讨论】:

    标签: stomp spring-websocket


    【解决方案1】:

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

    如果您订阅以“/user/”为前缀的目标并使用org.springframework.messaging.simp.SimpMessagingTemplate#convertAndSendToUserorg.​springframework.​messaging.​simp.​annotation.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-stomp-user-destination

    编辑:

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

    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());
    }
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-11-09
      • 2018-07-24
      • 2016-05-06
      • 2020-06-17
      • 2016-06-28
      • 2010-12-09
      • 2023-03-17
      相关资源
      最近更新 更多