问题描述
我的情况
我正在构建一个小型网络聊天来了解 Spring 和 Spring WebSocket。
您可以创建不同的房间,每个房间在/topic/room/{id}
都有自己的频道。
我的目标是检测用户何时加入和离开聊天室,我想我可以为此使用 Spring WebSocket 的SessionSubscribeEvent
和SessionUnsubscribeEvent
。
从SessionSubscribeEvent
获取目的地很简单:
@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();
//...
}
我的问题
是否有更好的方法来观看订阅/取消订阅事件,我什至应该使用它们作为用户“登录”聊天室的一种方式,还是应该使用单独的频道来发送单独的“登录” /“注销”消息并使用这些消息?
我认为使用订阅/取消订阅会非常方便,但显然 Spring 让它变得非常困难,所以我觉得必须有更好的方法。
1楼
STOMP 标头仅出现在与您的问题相关的框架中,如下所述: : 和此处: :
只有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));
2楼
我认为使用 SessionSubscribeEvent 和 SessionUnsubscribeEvent 是一个好主意。 如果您跟踪 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());
//...
}