java 向Spring WebSocket上的特定用户发送消息

exdqitrt  于 2022-12-28  发布在  Java
关注(0)|答案(6)|浏览(271)
    • 如何将websocket消息从服务器发送到特定用户?**

我的网络应用程序有spring安全设置,并使用websocket。我遇到了棘手的问题,试图发送消息从服务器到特定用户只
我从阅读手册的理解是从服务器我们可以做

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

在客户端:

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

但是我从来没有得到订阅回调调用。我已经尝试了许多不同的路径,但没有运气。
如果我将它发送到**/topic/reply**,它会工作,但所有其他连接的用户也会收到它。
为了说明这个问题,我在github上创建了这个小项目:https://github.com/gerrytan/wsproblem

    • 复制步骤:**

1)克隆并构建项目(确保您使用的是jdk1.7和maven3.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}"显示在仅针对此用户的"仅接收给我的消息"旁边,实际:未收到任何内容

ttvkxqim

ttvkxqim1#

哦,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 idMap到用户admin。例如:它发现了两个会话wsxedc123thnujm456,Spring会将其转换为两个目的地queue/reply-userwsxedc123queue/reply-userthnujm456,并将带有两个目的地的消息发送到消息代理。
消息代理接收消息并将其提供回您的服务器示例,该示例持有与每个会话对应的会话(WebSocket会话可以由一个或多个服务器持有). Spring会将消息转换为destination(例如:user/queue/reply)和session id(例如:wsxedc123),然后将消息发送到对应的Websocket session

zqdjd7g9

zqdjd7g92#

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

uemypmqf3#

我也使用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中,它都有效。

3ks5zfa0

3ks5zfa04#

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

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

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

u0njafvf5#

完全一样,我做了同样的,它是工作,而不使用用户

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

ulmd4ohb6#

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

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

网络套接字配置.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");
    }
}

网络套接字控制器.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;
    }
}

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

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

**客户端(带有react-stomp的Reactjs)**应用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/sockethttp://localhost:9090是API网关IP地址,notification-service是微服务的名称,/socketWebSocketConfig.java中定义。

相关问题