Java 在 Spring Websocket 上向特定用户发送消息

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22367223/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 15:17:47  来源:igfitidea点击:

Sending message to specific user on Spring Websocket

javaspringspring-mvcspring-websocket

提问by gerrytan

How to send websocket message from server to specific user only?

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

My webapp has spring security setup and uses websocket. I'm encountering tricky problem trying to send message from server to specific user only.

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

My understanding from reading the manualis from the server we can do

我阅读手册的理解来自我们可以做的服务器

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

And on the client side:

而在客户端:

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

But I could never get the subscription callback invoked. I have tried many different path but no luck.

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

If I send it to /topic/replyit works but all other connected users will receive it too.

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

To illustrate the problem I've created this small project on github: https://github.com/gerrytan/wsproblem

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

Steps to reproduce:

重现步骤:

1) Clone and build the project (make sure you're using jdk 1.7 and maven 3.1)

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

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

2) Navigate to http://localhost:8080, login using either bob/test or jim/test

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

3) Click "Request user specific msg". Expected: a message "hello {username}" is displayed next to "Received Message To Me Only" for this user only, Actual: nothing is received

3) 单击“请求用户特定信息”。预期:仅此用户的“仅收到给我的消息”旁边会显示一条消息“你好 {用户名}”,实际:未收到任何消息

回答by gerrytan

Ah I found what my problem was. First I didn't register the /userprefix on the simple broker

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

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

Then I don't need the extra /userprefix when sending:

然后我/user在发送时不需要额外的前缀:

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

Spring will automatically prepend "/user/" + principal.getName()to the destination, hence it resolves into "/user/bob/reply".

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

This also means in javascript I had to subscribe to different address per user

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

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

回答by Thanh Nguyen Van

Oh, client side no need to known about current user, server will do that for you.

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

On server side, using following way to send message to an user:

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

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

Note: Using queue, not topic, Spring always using queuewith sendToUser

注意:使用queue,而不是topic,Spring 总是使用queuewithsendToUser

On client side

在客户端

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

Explain

解释

When any websocket connection is open, Spring will assign it a session id(not HttpSession, assign per connection). And when your client subscribe to an channel start with /user/, eg: /user/queue/reply, your server instance will subscribe to a queue named queue/reply-user[session id]

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

When use send message to user, eg: username is adminYou will write simpMessagingTemplate.convertAndSendToUser("admin", "/queue/reply", message);

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

Spring will determine which session idmapped to user admin. Eg: It found two session wsxedc123and thnujm456, Spring will translate it to 2 destination queue/reply-userwsxedc123and queue/reply-userthnujm456, and it send your message with 2 destinations to your message broker.

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

The message broker receive the messages and provide it back to your server instance that holding session corresponding to each session (WebSocket sessions can be hold by one or more server). Spring will translate the message to destination(eg: user/queue/reply) and session id(eg: wsxedc123). Then, it send the message to corresponding Websocket session

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

回答by Nikolay Shabak

My solution of that based on Thanh Nguyen Van's best explanation, but in addition I have configured MessageBrokerRegistry:

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

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

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

回答by Kans

I created a sample websocket project using STOMP as well. What I am noticing is that

我也使用 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();
}

}

}

it works whether or not "/user" is included in config.enableSimpleBroker(...

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

回答by Sid

Exactly i did the same and it is working without using user

正是我做了同样的事情,它在不使用用户的情况下工作

@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");
    }
}