springbootjava
在利用websocket主動推送信息給客戶端的過程當中,常常會遇到一個廣泛需求,就是推送的消息要定向推送給不一樣的用戶,或者解釋的再普通一點,不一樣的消息推送給不一樣的session。例如一個用戶admin,能夠在多臺設備登陸,此時就有多個session,當一個設備向後臺發起一個請求,處理時間較長(未採用客戶端進行ajax輪訓或者long poll時),採用websocket協議時,要針對不一樣session的操做定向返回操做結果。
下面來具體看看如何實現上述需求。web
springboot對websocket支持很友好,只須要繼承webSocketHandler類,重寫幾個方法就能夠了ajax
public class ResultHandler extends TextWebSocketHandler { private static final Logger logger = LoggerFactory.getLogger(ResultHandler.class); @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { } @Override public void afterConnectionEstablished(WebSocketSession webSocketSession) throws Exception { } @Override public void afterConnectionClosed(WebSocketSession webSocketsession, CloseStatus status) throws Exception { } }
而後寫一個簡單的配置類spring
@EnableWebSocket@Configurationpublic class WebSocketConfig implements WebSocketConfigurer{ @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) { webSocketHandlerRegistry.addHandler(resultHandler (), "/getResult").withSockJS(); } @Bean public ResultHandler resultHandler(){ return new ResultHandler(); } }
這樣一個websocket 服務端就寫好了springboot
咱們但願可以把websocketSession和httpsession對應起來,這樣就能根據當前不一樣的session,定向對websocketSession進行數據返回
如何解決這個問題呢,考慮到websocket是在http的基礎上創建起來的(具體websocket創建,自行百度),能不能在websocket握手的時候,把當前的sessionId放入websocketSession的屬性中呢?websocket
在查詢資料以後,發現spring中有一個攔截器接口,HandshakeInterceptor,能夠實現這個接口,來攔截握手過程,向其中添加屬性session
public class WebSocketHandshakeInterceptor implements HandshakeInterceptor{ private static final Logger logger = LoggerFactory.getLogger(WebSocketHandshakeInterceptor.class); @Override public boolean beforeHandshake(ServerHttpRequest serverHttpRequest,ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map<String, Object> map) throws Exception { if(serverHttpRequest instanceof ServletServerHttpRequest){ ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) serverHttpRequest; HttpSession httpSession = servletRequest.getServletRequest().getSession(true); if(null != httpSession){ map.put(Constants.SESSION_ID,httpSession.getId()); } } return true; } @Override public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) { } }
beforeHandShake方法中的Map參數 就是對應websocketSession裏的屬性,向裏面添加內容後,能夠在上面的resultHandler裏面利用websocketSession參數將其取出來 String sessionId = (String)webSocketsession.getAttributes().get(Constants.SESSION_ID);框架
參考下面代碼socket
public class ResultHandler extends TextWebSocketHandler { private static final Logger logger = LoggerFactory.getLogger(ResultHandler.class); @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { } @Override public void afterConnectionEstablished(WebSocketSession webSocketSession) throws Exception { if(null != webSocketSession ){ String sessionId = (String)webSocketSession.getAttributes().get(Constants.SESSION_ID); addConnectionCount(); logger.info("WebSocket connection established, sessionId={} ConnectCount={}", sessionId, getConnectionCount()); } } @Override public void afterConnectionClosed(WebSocketSession webSocketsession, CloseStatus status) throws Exception { if(null != webSocketsession ){ String sessionId = (String)webSocketsession.getAttributes().get(Constants.SESSION_ID); SocketSessionManager.socketSessionMap.remove(sessionId); subConnectionCount(); logger.info("WebSocket connection close, Status={}, connectionCount={}", status, getConnectionCount()); } } public static synchronized int getConnectionCount (){ return ScriptTaskResultHandler.connectionCount; } private static synchronized void addConnectionCount(){ ScriptTaskResultHandler.connectionCount++; } private static synchronized void subConnectionCount(){ ScriptTaskResultHandler.connectionCount--; } }
還能夠本身寫一個webSocketSession管理類,當鏈接創建好以後,保存在這個管理類中,用sessionId當作Key,webSocketSession當作value,當結果處理完成後,根據sessionId找到相應的webSocketSession,進行數據推送。ide
攔截器加入的方法
@EnableWebSocket@Configurationpublic class WebSocketConfig implements WebSocketConfigurer{ @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) { webSocketHandlerRegistry.addHandler(resultHandler (), "/getResult"). addInterceptors(new WebSocketHandshakeInterceptor()).withSockJS(); } @Bean public ResultHandler resultHandler(){ return new ResultHandler(); } }