繁体   English   中英

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

[英]Sending message to specific user on Spring Websocket

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

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

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

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}”,实际:未收到任何消息

哦, client side no need to known about current user ,服务器会为您做到这一点。

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

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

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

在客户端

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

说明

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

当使用向用户发送消息时,例如:username是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

啊,我发现了我的问题。 首先,我没有在简单代理上注册/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,...) 

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

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

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

我也使用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中(...

我完全相同,它没有使用用户工作

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

在下面的解决方案中,我为客户端和后端编写了代码片段。 我们需要将/user放在客户端代码的套接字主题的开头。 否则,客户端无法监听套接字。 依赖性

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

WebSocketConfig.java

package com.oktaykcr.notificationservice.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

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

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/file");
        registry.setApplicationDestinationPrefixes("/app");
    }
}

WebSocketContoller.java

package com.oktaykcr.notificationservice.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;

@Controller
public class WebSocketController {

    Logger logger = LoggerFactory.getLogger(WebSocketController.class);

    @MessageMapping("/socket")
    @SendTo("/file/status")
    public String fileStatus(@Payload String message) {
        logger.info(message);
        return message;
    }
}

您可以从任何地方向套接字发送消息。 在我的例子中,我的 principal.getName() 等于 userId。

simpMessagingTemplate.convertAndSendToUser(userId, "/file/status", socketMessage);

客户端(带有react-stomp 的ReactJs) App.js

import './App.css';
import SockJsClient from 'react-stomp'
import { useRef } from 'react';

function App() {

  const clientRef = useRef();

  const sendMessage = (msg) => {
    clientRef.current.sendMessage('/app/socket', msg);
  }

  return (
    <div className="App">
      <div>
        <button onClick={() => sendMessage("Hola")}>Send</button>
      </div>
      <SockJsClient url='http://localhost:9090/notification-service/socket' topics={['/user/file/status']}
        onMessage={(msg) => { console.log(msg); }}
        ref={(client) => { clientRef.current = client }} />
    </div>
  );
}

export default App;

SockJsClient 元素的url属性是http://localhost:9090/notification-service/socket http://localhost:9090是 Api 网关 ip 地址, notification-service是微服务的名称, /socketWebSocketConfig.java中定义。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM