是否可以向特定会话发送消息?
我在客户端和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方法会很棒。
@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队列,你可以使用以下命令将消息发送到单个会话:
/user/queue/something
如SimpMessageSendingOperations Javadoc中所述,由于你的用户名实际上是一个sessionId,因此你还必须将其设置为标头,否则DefaultUserDestinationResolver将无法路由该消息并将其丢弃。
DefaultUserDestinationResolver
SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor .create(SimpMessageType.MESSAGE); headerAccessor.setSessionId(sessionId); headerAccessor.setLeaveMutable(true); messagingTemplate.convertAndSendToUser(sessionId,"/queue/something", payload, headerAccessor.getMessageHeaders());
你不需要为此认证用户。