当前位置: 首页 > 面试题库 >

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?
    }
}

如你在此代码中看到的,客户端可以启动异步作业,完成后,它需要结束消息。显然,我只需要向申请人发送消息,而不是向所有人广播。拥有@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));
                });
            });
        }

不幸的是,客户端没有按预期每隔5000毫秒收到一次会话响应。我确定“ 1”是连接的第二个客户端的有效sessionId,因为我在调试模式下看到SimpMessageHeaderAccessor.getSessionId()

背景场景

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

解决方案

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

从UPDATE2解决方案开始,我必须完成最后一个参数(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();
    }

问题答案:

给定用户订阅/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。我需要在异步作业结束时向特定连接发送未经请求的消息。 正如您在这段代码中看到的,客户端可以启动一个异步作业,当它完成时,它需要end消息。很明显,我只需要给申请人发信息,而不是向所有人广播。如果有批注或方法就太好了。 更新 我试过这个: 但这将广播到所有会话,而不仅仅是指定的会话。 更

  • 我目前正在使用Java和jda为discord制作一个机器人。我想让机器人向特定的通道发送消息。我该怎么做?

  • 问题内容: 我正在尝试从Android应用程序中向特定号码发送电报消息。现在,我的代码启动了Telegram应用程序,然后用户必须选择目的地。我要做的是将消息发送到指定的号码,而无需用户选择联系人。我的代码如下: 问题答案: Telegram Android应用程序无法直接向电报用户发送消息,因此,如果您使用共享意图,您将获得电报/任何其他应用程序想要处理的共享消息。在这种情况下,请打开联系人列表

  • 我想发送数据到一个特定的套接字ID。 在旧版本中,我们曾经能够做到这一点: 我将如何在Socket.io1.0中执行类似的操作?

  • 问题内容: 向所有客户端发送消息效果很好,但是我想向特定的用户名发送消息。我的server.js文件看起来像。它的作用是在运行时,客户端代码将用户添加到对象用户名以及套接字对象中。并立即将单个消息返回给每个连接的客户端。 发出消息的部分 问题答案: 试试这个: socket.id由socket.io保存,并且包含客户端的唯一ID。您可以使用以下命令进行检查:

  • 在socket.io中,如果要将消息发送到特定的房间,通常在服务器端使用特定的语法:。 但是客户机(我指的是在浏览器中运行的socket.io相关代码)如何指示消息应该发送到特定的房间? 还是socket.io客户端库也为此目的提供了特定的语法? 编辑:澄清一下,我上面的建议似乎起作用了,我只是不确定是否有更好的解决方案。