Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 检测SessionUnsubscribeEvent的目标通道 我的处境_Java_Spring_Websocket_Spring Websocket - Fatal编程技术网

Java 检测SessionUnsubscribeEvent的目标通道 我的处境

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

我正在建立一个小的网络聊天来了解Spring和SpringWebSocket。您可以创建不同的房间,每个房间都有自己的频道,位于
/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。我已经测试过了,现在还可以用,不过我还是觉得必须有一个比挖头更好的方法。。。