Java 检测SessionUnsubscribeEvent的目标通道 我的处境
我正在建立一个小的网络聊天来了解Spring和SpringWebSocket。您可以创建不同的房间,每个房间都有自己的频道,位于Java 检测SessionUnsubscribeEvent的目标通道 我的处境,java,spring,websocket,spring-websocket,Java,Spring,Websocket,Spring Websocket,我正在建立一个小的网络聊天来了解Spring和SpringWebSocket。您可以创建不同的房间,每个房间都有自己的频道,位于/topic/room/{id} 我的目标是检测用户何时加入和离开聊天室,我想我可以使用SpringWebSocket的SessionSubscribeeEvent和SessionUnsubscribeeEvent来实现这一点 从会话SubscribeeEvent获取目标非常简单: @EventListener public void handleSubscribe(f
/topic/room/{id}
我的目标是检测用户何时加入和离开聊天室,我想我可以使用SpringWebSocket的SessionSubscribeeEvent
和SessionUnsubscribeeEvent
来实现这一点
从会话SubscribeeEvent
获取目标非常简单:
@EventListener
public void handleSubscribe(final SessionSubscribeEvent event) {
final String destination =
SimpMessageHeaderAccessor.wrap(event.getMessage()).getDestination();
//...
}
但是,SessionUnsubscribeEvent
似乎没有携带目标频道,destination
在以下代码段中为null
:
@EventListener
public void handleUnsubscribe(final SessionUnsubscribeEvent event) {
final String destination =
SimpMessageHeaderAccessor.wrap(event.getMessage()).getDestination();
//...
}
我的问题
有没有更好的方法来监视订阅/取消订阅事件?我是否应该使用它们作为用户“登录”聊天室的一种方式,还是应该使用单独的频道发送单独的“登录”/“注销”消息并处理这些消息
我原以为使用subscribe/unsubscribe会非常方便,但显然Spring让它变得非常困难,所以我觉得必须有更好的方法。我认为使用SessionSubscribeeEvent和SessionUnsubscribeeEvent是一个好主意。如果跟踪SessionID,则可以获取目的地:
private Map<String, String> destinationTracker = new HashMap<>();
@EventListener
public void handleSubscribe(final SessionSubscribeEvent event) {
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(event.getMessage());
destinationTracker.put(headers.getSessionId(), headers.getDestination());
//...
}
@EventListener
public void handleUnsubscribe(final SessionUnsubscribeEvent event) {
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(event.getMessage());
final String destination = destinationTracker.get(headers.getSessionId());
//...
}
private Map destinationTracker=new HashMap();
@事件监听器
公共无效可处理订阅(最终会话订阅事件){
SimpMessageHeaderAccessor headers=SimpMessageHeaderAccessor.wrap(event.getMessage());
destinationTracker.put(headers.getSessionId(),headers.getDestination());
//...
}
@事件监听器
public void handleUnsubscribe(最终会话Unsubscribe事件){
SimpMessageHeaderAccessor headers=SimpMessageHeaderAccessor.wrap(event.getMessage());
最终字符串destination=destinationTracker.get(headers.getSessionId());
//...
}
跺脚标题仅出现在与您的问题相关的框架中,如下所述:
只有SUBSCRIBE
帧同时具有目的地和id,而UNSUBSCRIBE
帧仅具有id。
这意味着您必须记住带有目标的订阅id,以便将来查找。必须小心,因为不同的Websocket连接通常使用/分配相同的订阅id,所以为了可靠地保存目的地,必须在存储密钥中包含Websocket会话id
我编写了以下方法来获得它:
protected String getWebsocketSessionId(StompHeaderAccessor headerAccessor)
{
// SimpMessageHeaderAccessor.SESSION_ID_HEADER seems to be set in StompSubProtocolHandler.java:261 ("headerAccessor.setSessionId(session.getId());")
return headerAccessor.getHeader(SimpMessageHeaderAccessor.SESSION_ID_HEADER).toString();
}
StompHeaderAccessor
是这样创建的:
StompHeaderAccessor headerAccessor=StompHeaderAccessor.wrap(((SessionSubscribeEvent)event).getMessage());
StompHeaderAccessor headerAccessor=StompHeaderAccessor.wrap(((SessionUnsubscribeEvent)event).getMessage());
然后,可以使用此选项创建唯一的订阅id,该id可用作映射的键,以保存有关订阅的数据,包括目标:
protected String getUniqueSubscriptionId(StompHeaderAccessor headerAccessor)
{
return getWebsocketSessionId(headerAccessor)+"--"+headerAccessor.getSubscriptionId();
}
像这样:
Map<String, String> destinationLookupTable=...;
// on subscribe:
destinationLookupTable.put(getUniqueSubscriptionId(headerAccessor), destination);
// on other occasions, including unsubscribe:
destination=destinationLookupTable.get(getUniqueSubscriptionId(headerAccessor));
Map destinationLookupTable=。。。;
//订阅时:
destinationLookupTable.put(getUniqueSubscriptionId(headerAccessor),destination);
//在其他情况下,包括退订:
destination=destinationLookupTable.get(getUniqueSubscriptionId(headerAccessor));
如果客户订阅了其他频道,会发生什么情况?我还有其他频道,可以播放可用房间之类的内容。或者,一个客户端可以订阅多个房间。在这种情况下,您将在destinationTracker中看到最后一个目的地。如果这对您很重要,您可能希望使用列表而不是字符串作为值类型。如果您这样做,请不要忘记删除取消订阅的频道。但传入订阅的顺序与取消订阅的(相反)顺序不匹配,因此,不幸的是,我看不到频道与SessionUnsubscribeEvent
之间如何存在映射。我明白您的问题。不幸的是,我现在无法测试,但我认为您最好的选择是检查-可能subscribtionId头已设置,或者您可以使用其他方法来区分不同的频道?好的。标题有一个键nativeHeaders
,这会导致另一个包含键id
的映射,该映射指向一个LinkedList
,该链接列表具有每个用户唯一的频道id。我已经测试过了,现在还可以用,不过我还是觉得必须有一个比挖头更好的方法。。。