我想了解convertAndSendToUser如何在Spring SockJS Websocket框架中工作.
在客户端,我们将连接为
stompClient.connect(login, password, callback())
这将导致具有登录和密码的“Stomp凭证”的连接请求,例如可以看到如果我们处理SessionConnectEvent http://www.sergialmar.com/2014/03/detect-websocket-connects-and-disconnects-in-spring-4/
但我仍然不清楚这是否是服务器端向队列发送操作的“用户”:
simpMessagingTemplate.convertAndSendToUser(username, "/queue/reply", message);
我能得到的最接近的是阅读这篇帖子Sending message to specific user on Spring Websocket,由Thanh Nguyen Van回答,但目前还不清楚.
基本上我需要做的是订阅一些客户端到同一主题,但在服务器上,发送不同的数据.客户可以提供用户标识符.
解决方法:
我一直试图理解websockets并遇到了这个问题.没有找到答案,我感到非常失望.这是未来读者的一些东西.
我将假设读者对使用Stomp的弹簧腹板有基本的了解.诸如订阅,目的地前缀,主题,套接字配置文件等术语被理解.
我们知道我们可以使用他订阅的主题前缀从stomp服务器向客户端发送消息,例如/主题/你好.我们还知道我们可以向特定用户发送消息,因为spring提供了convertAndSendToUser(用户名,目标,消息)API.它接受一个String用户名,这意味着如果我们以某种方式为每个连接都有一个唯一的用户名,我们应该能够向订阅主题的特定用户发送消息.
不太了解的是,这个用户名来自哪里?
此用户名是java.security.Principal接口的一部分.每个StompHeaderAccessor或WebSocketSession对象都有此主体的实例,您可以从中获取用户名.但是,根据我的实验,它不是自动生成的.它必须由服务器为每个会话手动生成.
要首先使用此接口,您需要实现它.
class StompPrincipal implements Principal {
String name
StompPrincipal(String name) {
this.name = name
}
@Override
String getName() {
return name
}
}
然后,您可以通过覆盖DefaultHandshakeHandler为每个连接生成唯一的StompPrincipal.您可以使用任何逻辑来生成用户名.这是一个使用UUID的潜在逻辑:
class CustomHandshakeHandler extends DefaultHandshakeHandler {
// Custom class for storing principal
@Override
protected Principal determineUser(ServerHttpRequest request,
WebSocketHandler wsHandler,
Map<String, Object> attributes) {
// Generate principal with UUID as name
return new StompPrincipal(UUID.randomUUID().toString())
}
}
最后,您需要配置websockets以使用自定义握手处理程序.
@Override
void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
stompEndpointRegistry
.addEndpoint("/stomp") // Set websocket endpoint to connect to
.setHandshakeHandler(new CustomHandshakeHandler()) // Set custom handshake handler
.withSockJS() // Add Sock JS support
}
而已.现在,您的服务器配置为为每个连接生成唯一的主体名称.它将作为StomHeaderAccessor对象的一部分传递该主体,您可以通过连接事件侦听器,MessageMapping函数等访问它们…
来自事件监听器:
@EventListener
void handleSessionConnectedEvent(SessionConnectedEvent event) {
// Get Accessor
StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage())
}
来自消息映射的API
@MessageMapping('/hello')
protected void hello(SimpMessageHeaderAccessor sha, Map message) {
// sha available in params
}
关于使用convertAndSendToUser(…)的最后一点说明.向用户发送消息时,您将使用类似的内容
convertAndSendToUser(sha.session.principal.name, '/topic/hello', message)
但是,对于订阅客户端,您将使用
client.subscribe('/user/topic/hello', callback)
如果您将客户端订阅到/ topic / hello,您将只收到广播的消息.