当前位置: 首页 > 知识库问答 >
问题:

spring WebSocket@SendToSession:向特定会话发送消息

狄旻
2023-03-14

是否可以向特定会话发送消息?

我在客户端和一个spring servlet之间有一个未经认证的websocket。我需要在异步作业结束时向特定连接发送未经请求的消息。

@Controller
public class WebsocketTest {


     @Autowired
    public SimpMessageSendingOperations messagingTemplate;

    ExecutorService executor = Executors.newSingleThreadExecutor();

    @MessageMapping("/start")
    public void start(SimpMessageHeaderAccessor accessor) throws Exception {
        String applicantId=accessor.getSessionId();        
        executor.submit(() -> {
            //... slow job
            jobEnd(applicantId);
        });
    }

    public void jobEnd(String sessionId){
        messagingTemplate.convertAndSend("/queue/jobend"); //how to send only to that session?
    }
}

正如您在这段代码中看到的,客户端可以启动一个异步作业,当它完成时,它需要end消息。很明显,我只需要给申请人发信息,而不是向所有人广播。如果有@SendtoSession批注或MessagingTemplate.ConvertandSendtoSession方法就太好了。

更新

我试过这个:

messagingTemplate.convertAndSend("/queue/jobend", true, Collections.singletonMap(SimpMessageHeaderAccessor.SESSION_ID_HEADER, sessionId));

但这将广播到所有会话,而不仅仅是指定的会话。

更新2

使用convertAndSendToUser()方法进行测试。这个测试是spring官方教程的hack:https://Spring.io/guides/gs/messaging-stomp-websocket/

这是服务器代码:

@Controller
public class WebsocketTest {

    @PostConstruct
    public void init(){
        ScheduledExecutorService statusTimerExecutor=Executors.newSingleThreadScheduledExecutor();
        statusTimerExecutor.scheduleAtFixedRate(new Runnable() {                
            @Override
            public void run() {
                messagingTemplate.convertAndSendToUser("1","/queue/test", new Return("test"));
            }
        }, 5000,5000, TimeUnit.MILLISECONDS);
    } 

     @Autowired
        public SimpMessageSendingOperations messagingTemplate;
}

这是客户端代码:

function connect() {
            var socket = new WebSocket('ws://localhost:8080/hello');
            stompClient = Stomp.over(socket);
            stompClient.connect({}, function(frame) {
                setConnected(true);
                console.log('Connected: ' + frame);
                stompClient.subscribe('/user/queue/test', function(greeting){
                    console.log(JSON.parse(greeting.body));
                });
            });
        }

不幸的是,客户端并没有像预期的那样每5000ms收到一次会话回复。我确信“1”是连接的第二个客户端的有效会话ID,因为我在调试模式下使用SimpMessageHeaderAccessor.GetSessionID()看到它

背景场景

我想为一个远程作业创建一个进度条,客户端向服务器请求一个异步作业,它通过服务器发送的websocket消息来检查它的进度。这不是一个文件上传而是一个远程计算,所以只有服务器知道每个作业的进度。我需要发送一个消息到特定的会话,因为每个作业是由会话启动的。客户端请求远程计算服务器启动此作业,并且对于每个作业步骤,将其作业进度状态回复申请者客户端。客户端获取关于其作业的消息并建立进度/状态栏。这就是为什么我需要一个每个会话的消息。我也可以使用每个用户的消息,但spring不提供每个用户未经请求的消息。(无法用spring Websocket发送用户消息)

工作溶液

 __      __ ___   ___  _  __ ___  _  _   ___      ___   ___   _    _   _  _____  ___  ___   _  _ 
 \ \    / // _ \ | _ \| |/ /|_ _|| \| | / __|    / __| / _ \ | |  | | | ||_   _||_ _|/ _ \ | \| |
  \ \/\/ /| (_) ||   /| ' <  | | | .` || (_ |    \__ \| (_) || |__| |_| |  | |   | || (_) || .` |
   \_/\_/  \___/ |_|_\|_|\_\|___||_|\_| \___|    |___/ \___/ |____|\___/   |_|  |___|\___/ |_|\_|

从UPDATE2解决方案开始,我必须使用last param(MessageHeaders)完成convertAndSendToUser方法:

messagingTemplate.convertAndSendToUser("1","/queue/test", new Return("test"), createHeaders("1"));

其中createHeaders()是此方法:

private MessageHeaders createHeaders(String sessionId) {
        SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
        headerAccessor.setSessionId(sessionId);
        headerAccessor.setLeaveMutable(true);
        return headerAccessor.getMessageHeaders();
    }

共有2个答案

澹台俊晖
2023-03-14

这是非常复杂的,在我看来,是不值得的。您需要根据每个用户(即使是未经身份验证的用户)的会话ID为他们创建一个订阅。

假设每个用户只为他订阅一个唯一队列:

stompClient.subscribe('/session/specific' + uuid, handler);

在服务器上,在用户订阅之前,您需要通知并发送特定会话的消息,并保存到映射:

    @MessageMapping("/putAnonymousSession/{sessionId}")
    public void start(@DestinationVariable sessionId) throws Exception {
        anonymousUserSession.put(key, sessionId);
    }

之后,当您要向用户发送消息时,您将需要:

messagingTemplate.convertAndSend("/session/specific" + key); 

但我并不真正知道你正在尝试做什么,以及你将如何找到特定的会话(谁是匿名的)。

越勇
2023-03-14

不需要创建特定的目的地,它已经在spring 4.1开箱即用(参见SPR-11309)。

给定用户订阅/user/queue/something队列,您可以使用以下命令向单个会话发送消息:

正如SimpMessageSendingOperations Javadoc中所述,由于您的用户名实际上是一个sessionId,您必须将其设置为头,否则DefaultUserDestinationResolver将无法路由消息并将其丢弃。

SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor
    .create(SimpMessageType.MESSAGE);
headerAccessor.setSessionId(sessionId);
headerAccessor.setLeaveMutable(true);

messagingTemplate.convertAndSendToUser(sessionId,"/queue/something", payload, 
    headerAccessor.getMessageHeaders());

您不需要为此对用户进行身份验证。

 类似资料:
  • 问题内容: 是否可以向特定会话发送消息? 我在客户端和Spring Servlet之间有未经身份验证的WebSocket。异步作业结束时,我需要向特定的连接发送未经请求的消息。 如你在此代码中看到的,客户端可以启动异步作业,完成后,它需要结束消息。显然,我只需要向申请人发送消息,而不是向所有人广播。拥有注释或方法会很棒。 更新 我尝试了这个: 但这会广播到所有会话,而不仅仅是指定的会话。 更新2

  • 我已经按照Quetion1和Quetion2从堆栈溢出发送消息到特定的客户端,基于其会话ID,但找不到成功。 下面是我的示例RestController类 会话ID:当客户端发送create会话请求时,会生成新的Spring会话ID,并将其存储在MongoDB中。之后,当客户端发送Web套接字连接请求时,会收到与预期存储在mongoDb中的相同的会话ID。直到一切正常。 现在,我的工作是根据Ses

  • 我是新手。NET(C#)和WebSocket。。。我已经安装了VS2012和Windows Server 2012,并且已经启动并运行了WebSocket。我似乎无法从一个套接字接收消息并将其发送到另一个特定的套接字。唯一的选择似乎是向所有套接字广播消息。有没有一种方法可以将消息只发送给特定的用户?我希望聊天室主持人有机会拒绝不适当的帖子。

  • 我使用Spring的STOMP over WebSocket实现与一个全功能的ActiveMQ代理。当用户向主题订阅时,在成功订阅之前必须通过一些权限逻辑。我使用ChannelInterceptor应用权限逻辑,如下所示: WebSocketConfig。爪哇: WebSocketSecurityConfig.java: MySubscriptionInterceptor。爪哇: 当没有足够权限的

  • 我使用本教程中描述的spring设置了一个WebSocket:https://spring.io/guides/gs/messaging-stomp-websocket/。我需要的是我的服务器每5秒向特定用户发出一条消息。所以我首先做了这个: 而且起作用了。现在,为了只向特定用户发送消息,我更改了以下内容: 在WebSocketConfig.java中添加队列: 更改GreetingControl

  • 假设我在MongoDB中有以下模式: 每个帖子必须有一个所有者,并且可能有许多贡献者(由所有者添加)。 当所有者或任何贡献者对post对象进行更改时,我需要将该更改实时通知给所有相关的用户(使用Socket。IO)。 但是我对如何做到这一点有一些疑问...我的意思是,我知道 Socket.IO 有一些方法可以向特定用户发出消息,但我们需要知道 SocketID。如何将用户 ID 与套接字 ID 相