webscoket實戰之利用httpsession定向推送


webscoket實戰之利用httpsession定向推送

開發框架

springboot

場景

在利用websocket主動推送信息給客戶端的過程中,經常會遇到一個普遍需求,就是推送的消息要定向推送給不同的用戶,或者解釋的再普通一點,不同的消息推送給不同的session。例如一個用戶admin,可以在多台設備登錄,此時就有多個session,當一個設備向后台發起一個請求,處理時間較長(未采用客戶端進行ajax輪訓或者long poll時),采用websocket協議時,要針對不同session的操作定向返回操作結果。
下面來具體看看如何實現上述需求。

實例

springboot對websocket支持很友好,只需要繼承webSocketHandler類,重寫幾個方法就可以了

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 {
       
    }
}

然后寫一個簡單的配置類

@EnableWebSocket
@Configuration
public class WebSocketConfig implements WebSocketConfigurer{

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
        webSocketHandlerRegistry.addHandler(resultHandler (), "/getResult").withSockJS();
    }

    @Bean
    public ResultHandler resultHandler(){
        return new ResultHandler();
    }

}

這樣一個websocket 服務端就寫好了

我們希望能夠把websocketSession和httpsession對應起來,這樣就能根據當前不同的session,定向對websocketSession進行數據返回
如何解決這個問題呢,考慮到websocket是在http的基礎上建立起來的(具體websocket建立,自行百度),能不能在websocket握手的時候,把當前的sessionId放入websocketSession的屬性中呢?

在查詢資料之后,發現spring中有一個攔截器接口,HandshakeInterceptor,可以實現這個接口,來攔截握手過程,向其中添加屬性

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

參考下面代碼

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,進行數據推送。

攔截器加入的方法

@EnableWebSocket
@Configuration
public 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();
    }

}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM