webscoket實戰之利用httpsession定向推送

    

開發框架

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();
    }

}
相關文章
相關標籤/搜索