java - mvc - Enviar mensaje a un usuario específico en Spring Websocket
websockets spring angular (5)
¿Cómo enviar un mensaje de websocket del servidor a un usuario específico solamente?
Mi aplicación web tiene configuración de seguridad de primavera y usa websocket. Me encuentro con un problema complicado tratando de enviar mensajes del servidor a usuarios específicos solamente .
Mi comprensión al leer el manual es del servidor que podemos hacer
simpMessagingTemplate.convertAndSend("/user/{username}/reply", reply);
Y en el lado del cliente:
stompClient.subscribe(''/user/reply'', handler);
Pero nunca podría obtener la devolución de llamada de suscripción invocada. He intentado muchos caminos diferentes, pero no tuve suerte.
Si lo envío a / topic / reply funciona, pero todos los demás usuarios conectados también lo recibirán.
Para ilustrar el problema, he creado este pequeño proyecto en github: https://github.com/gerrytan/wsproblem
Pasos para reproducir:
1) Clona y crea el proyecto (asegúrate de estar usando jdk 1.7 y maven 3.1)
$ git clone https://github.com/gerrytan/wsproblem.git
$ cd wsproblem
$ mvn jetty:run
2) Navegue a http://localhost:8080
, inicie sesión usando bob / test o jim / test
3) Haga clic en "Solicitar msg específico del usuario". Esperado: aparece un mensaje "hola {nombre de usuario}" junto a "Mensaje recibido solo para mí" para este usuario, real: no se recibe nada
Ah, encontré cuál era mi problema. Primero, no registré el prefijo /user
en el simple broker
<websocket:simple-broker prefix="/topic,/user" />
Entonces no necesito el prefijo extra /user
al enviar:
convertAndSendToUser(principal.getName(), "/reply", reply);
Spring agregará automáticamente "/user/" + principal.getName()
al destino, por lo que se convierte en "/ user / bob / reply".
Esto también significa que en javascript tuve que suscribirme a diferentes direcciones por usuario
stompClient.subscribe(''/user/'' + userName + ''/reply,...)
Creé un proyecto de websocket de muestra usando STOMP también. Lo que noto es que
@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();
}
}
funciona si se incluye o no "/ user" en config.enableSimpleBroker (...
Exactamente hice lo mismo y está funcionando sin usar usuario
@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");
}
}
Mi solución está basada en la mejor explicación de Thanh Nguyen Van, pero además he configurado MessageBrokerRegistry:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/queue/", "/topic/");
...
}
...
}
Oh, client side no need to known about current user
, el servidor lo hará por usted.
Del lado del servidor, usando la siguiente forma para enviar un mensaje a un usuario:
simpMessagingTemplate.convertAndSendToUser(username, "/queue/reply", message);
Nota: Usando queue
, no topic
, Spring siempre usando queue
con sendToUser
Del lado del cliente
stompClient.subscribe("/user/queue/reply", handler);
Explique
Cuando cualquier conexión de websocket está abierta, Spring le asignará una session id
(no HttpSession
, asignar por conexión). Y cuando su cliente se suscribe a un canal, comience con /user/
, por ejemplo: /user/queue/reply
, su instancia de servidor se suscribirá a una cola llamada queue/reply-user[session id]
Cuando use enviar mensaje al usuario, por ejemplo: username es admin
Escribirá simpMessagingTemplate.convertAndSendToUser("admin", "/queue/reply", message);
Spring determinará qué session id
asignó al admin
usuario. Por ejemplo: encontró dos sesiones wsxedc123
y thnujm456
, Spring la traducirá a 2 destinos queue/reply-userwsxedc123
y queue/reply-userthnujm456
, y enviará su mensaje con 2 destinos a su intermediario de mensajes.
El intermediario de mensajes recibe los mensajes y los devuelve a su instancia de servidor que contiene la sesión correspondiente a cada sesión (las sesiones de WebSocket pueden ser retenidas por uno o más servidores). Spring traducirá el mensaje a destination
(p. Ej .: user/queue/reply
) y la session id
(p. wsxedc123
.: wsxedc123
). Luego, envía el mensaje a la Websocket session
correspondiente de Websocket session