【问题标题】:Sending message to specific user on Spring Websocket在 Spring Websocket 上向特定用户发送消息
【发布时间】:2014-04-17 12:20:45
【问题描述】:

如何仅将 websocket 消息从服务器发送给特定用户?

我的 webapp 有 spring 安全设置并使用 websocket。我在尝试将消息从服​​务器发送到仅限特定用户时遇到了棘手的问题。

我阅读the manual的理解来自我们可以做的服务器

simpMessagingTemplate.convertAndSend("/user/{username}/reply", reply);

在客户端:

stompClient.subscribe('/user/reply', handler);

但我永远无法调用订阅回调。我尝试了许多不同的路径,但没有运气。

如果我将它发送到 /topic/reply,它会起作用,但所有其他连接的用户也会收到它。

为了说明问题,我在 github 上创建了这个小项目:https://github.com/gerrytan/wsproblem

重现步骤:

1) 克隆并构建项目(确保您使用的是 jdk 1.7 和 maven 3.1)

$ git clone https://github.com/gerrytan/wsproblem.git
$ cd wsproblem
$ mvn jetty:run

2) 导航到http://localhost:8080,使用 bob/test 或 jim/test 登录

3) 单击“请求用户特定消息”。预期:仅针对此用户的“仅接收给我的消息”旁边显示消息“hello {username}”,实际:未收到任何内容

【问题讨论】:

  • 你一直在看 convertAndSendToUser(String user, String destination, T message) 吗? docs.spring.io/spring/docs/4.0.0.M3/javadoc-api/org/…
  • 是的也试过了,但没有运气
  • 我一直在从事私人项目,这是我们使用的方法,它对我们有用。我认为一个潜在的问题可能是您订阅了“/user/reply”并且您正在向“/user/{username}/reply”发送消息。我认为您应该删除 {username} 部分并使用 convertAndSendToUser(String user, String destination, T message)。
  • 谢谢,但我试过simpMessagingTemplate.convertAndSendToUser(principal.getName(), "/user/reply", reply);,当从服务器发送消息时,它会抛出这个异常java.lang.IllegalArgumentException: Expected destination pattern "/principal/{userId}/**"
  • @ViktorK。是对的,你非常接近正确的解决方案。您在客户端的订阅是正确的,您只需尝试:convertAndSendToUser(principal.getName(), "/reply", reply);

标签: java spring spring-mvc spring-websocket


【解决方案1】:

我也是这样做的,而且它在不使用用户的情况下工作

@Configuration
@EnableWebSocketMessageBroker  
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
       registry.addEndpoint("/gs-guide-websocket").withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic" , "/queue");
        config.setApplicationDestinationPrefixes("/app");
    }
}

【讨论】:

  • 嘿,你在哪里用过这个/app?在哪种情况下?
【解决方案2】:

哦,client side no need to known about current user,服务器会帮你做的。

在服务器端,使用以下方式向用户发送消息:

simpMessagingTemplate.convertAndSendToUser(username, "/queue/reply", message);

注意:使用queue,而不是topic,Spring 始终使用queuesendToUser

在客户端

stompClient.subscribe("/user/queue/reply", handler);

解释

当任何 websocket 连接打开时,Spring 会为其分配一个session id(不是HttpSession,为每个连接分配)。当您的客户端订阅以/user/ 开头的频道时,例如:/user/queue/reply,您的服务器实例将订阅名为queue/reply-user[session id] 的队列

当使用发送消息给用户时,例如:用户名是admin 你会写simpMessagingTemplate.convertAndSendToUser("admin", "/queue/reply", message);

Spring 将确定哪个session id 映射到用户admin。例如:它找到了两个会话 wsxedc123thnujm456,Spring 会将其转换为 2 个目的地 queue/reply-userwsxedc123queue/reply-userthnujm456,并将您的带有 2 个目的地的消息发送到您的消息代理。

消息代理接收消息并将其提供回您的服务器实例,该实例持有与每个会话对应的会话(WebSocket 会话可以由一个或多个服务器持有)。 Spring 会将消息翻译为destination(例如:user/queue/reply)和session id(例如:wsxedc123)。然后,它将消息发送到相应的Websocket session

【讨论】:

  • 你能解释一下你是怎么知道用户名的吗?在您的示例中,您说用户名是“管理员”,您从用户那里收到用户名?
  • 用户名是在发起websocket连接时从HttpSession获取的
  • 请您提供更多关于如何设置用户名的信息?无论如何我可以在订阅消息中发送用户名吗?
  • @Andres:您可以扩展DefaultHandshakeHandler 并覆盖方法determineUser
  • 你的解释很好。但是官方文档中的queue/reply-user[session id]部分在哪里?
【解决方案3】:

我也使用 STOMP 创建了一个示例 websocket 项目。 我注意到的是

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
    config.enableSimpleBroker("/topic", "/queue");// including /user also works
    config.setApplicationDestinationPrefixes("/app");
}

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
    registry.addEndpoint("/getfeeds").withSockJS();
}

}

无论“/user”是否包含在 config.enableSimpleBroker(...

【讨论】:

    【解决方案4】:

    我的解决方案基于 Thanh Nguyen Van 的最佳解释,但另外我还配置了 MessageBrokerRegistry:

    @Configuration
    @EnableWebSocketMessageBroker
    public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
    
        @Override
        public void configureMessageBroker(MessageBrokerRegistry config) {
            config.enableSimpleBroker("/queue/", "/topic/");
            ...
        }
        ...
    }
    

    【讨论】:

      【解决方案5】:

      啊,我发现了我的问题所在。首先我没有在简单代理上注册/user前缀

      <websocket:simple-broker prefix="/topic,/user" />
      

      那我发送时不需要额外的/user前缀:

      convertAndSendToUser(principal.getName(), "/reply", reply);
      

      Spring 会自动将"/user/" + principal.getName() 添加到目的地,因此它会解析为“/user/bob/reply”。

      这也意味着在 javascript 中我必须为每个用户订阅不同的地址

      stompClient.subscribe('/user/' + userName + '/reply,...) 
      

      【讨论】:

      猜你喜欢
      • 2020-05-06
      • 2016-02-16
      • 2016-04-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-13
      • 1970-01-01
      • 2013-04-23
      相关资源
      最近更新 更多