Springboot-WebSocket初探-獲取HttpSession問題
阿新 • • 發佈:2018-03-20
turn mar 代碼 close ati nwr html5 解決方案 code
換了新工作,第一個任務就是和這個有關,以前沒接觸過,沒辦法,各種度娘、谷哥,大部分都是只言片語,要麽就是特定的配置環境還不貼配置……,踩坑無數, 遂整理成筆記
WebSocket協議
WebSocket是一種在單個TCP連接上進行全雙工通訊的協議。WebSocket通信協議於2011年被IETF定為標準RFC 6455,並由RFC7936補充規範。WebSocket API也被W3C定為標準。
WebSocket使得客戶端和服務器之間的數據交換變得更加簡單,允許服務端主動向客戶端推送數據。在WebSocket API中,瀏覽器和服務器只需要完成一次握手,兩者之間就直接可以創建持久性的連接,並進行雙向數據傳輸
STOMP協議
STOMP是面向文本的消息傳送協議。STOMP客戶端與支持STOMP協議的消息代理進行通信。STOMP使用不同的命令,如連接,發送,訂閱,斷開等進行通信。
具體參考:官方介紹
SockJS
SockJS是一個JavaScript庫,提供跨瀏覽器JavaScript的API,創建了一個低延遲、全雙工的瀏覽器和web服務器之間通信通道
以上內容出自維基百科和百度百科
環境配置
- SpringBoot2.0全家桶
- 特別說明一下,現在很多服務器都支持websocket,我這次寫代碼用的是SpringBoot內置的tomcat
應用場景
- 如果只是單純的配置一個建立連接發送消息,並不難,具體客戶端編寫參考這個 菜鳥教程-websocket
- 我需要解決的問題是:當客戶端和服務器建立連接時,將登錄的用戶信息從httpSession中取出,這個問題的難點在於,websocket的請求和http請求完全不相關,所以沒有辦法直接獲取HttpSession
實施思路
- 服務器端的編碼實現,具體有兩套方案
- 使用繼承的方式,這種方式,我在看Spring官方介紹文檔時貌似也是這種方式實現的
- 使用註解的方式:很蛋疼的是我們的項目使用的是這種方式
- 具體思路
- 雖然websocket的請求和http請求完全不相關,但是如果基於註解的話,EndPoint支持讀取一個配置
- 我當時的想法就是在配置中攔截或者獲取HttpSession,事實證明思路是正確的,但是當我按照這個思路去百度谷歌發現獲取的一律都是null,然後我就崩潰了
具體編碼實現
- 只貼核心代碼,完整項目在本文的底部,我放在了github上
先上核心的服務器端消息處理類
/** * WebSocket主要的消息類 * @author 侯葉飛 */ //onfigurator = WebsocketConfig.class 該屬性就是我上面提到我們可以自己配置的東西 @ServerEndpoint(value = "/api/websocket", configurator = WebsocketConfig.class) @Component @Slf4j public class WebSocket { /*每個瀏覽器連接都會有一個新的會話對象*/ private Session session; /*用來存儲每個會話的session,靜態的不會被實例化*/ private static CopyOnWriteArraySet<WebSocket> webSocketSets = new CopyOnWriteArraySet<>(); /** * 主要用來監聽連接建立,config用來獲取WebsocketConfig中的配置信息 * @param session * @param config */ @OnOpen public void onOpen(Session session, EndpointConfig config) { log.info("config:{}", config.getUserProperties().get("name")); log.info("session:{}", config.getUserProperties().get("sessionid")); this.session = session; webSocketSets.add(this); log.info("【websocket消息】有新的連接, 總數:{}", webSocketSets.size()); } @OnClose public void onClose() { webSocketSets.remove(this); log.info("【websocket消息】連接斷開, 總數:{}", webSocketSets.size()); } @OnError public void onError(Throwable e, Session session) { webSocketSets.remove(this); log.info("【websocket消息】連接出錯或未關閉socket:" + e.getMessage()); } @OnMessage public void onMessage(String message, Session session) { for(WebSocket ws:webSocketSets){ ws.session.getAsyncRemote().sendText("廣播:"+message); } log.info("【websocket消息】收到客戶端發來的消息:{}", message); } }
下面代碼就是核心的配置類
/** * 主要的配置類 * 本類必須要繼承Configurator,因為@ServerEndpoint註解中的config屬性只接收這個類型 * @author 侯葉飛 * */ @Configuration @Slf4j public class WebsocketConfig extends ServerEndpointConfig.Configurator { private static final String HttpSession = null; /* 修改握手,就是在握手協議建立之前修改其中攜帶的內容 */ @Override public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) { /*如果沒有監聽器,那麽這裏獲取到的HttpSession是null*/ StandardSessionFacade ssf = (StandardSessionFacade) request.getHttpSession(); if (ssf != null) { HttpSession session = (HttpSession) request.getHttpSession(); sec.getUserProperties().put("sessionid", session); log.info("獲取到的SessionID:{}",session.getId()); } sec.getUserProperties().put("name", "小強"); super.modifyHandshake(sec, request, response); } @Bean public ServerEndpointExporter serverEndpointExporter() { //這個對象說一下,貌似只有服務器是tomcat的時候才需要配置,具體我沒有研究 return new ServerEndpointExporter(); } }
- 僅僅有上面的配置獲取的肯定是null,至於原因,網上說法不一,我也不確定,解決方案如下
/** * 監聽器類:主要任務是用ServletRequest將我們的HttpSession攜帶過去 * @author 侯葉飛 */ @Component //此註解千萬千萬不要忘記,它的主要作用就是將這個監聽器納入到Spring容器中進行管理,相當於註冊監聽吧 @Slf4j public class RequestListener implements ServletRequestListener { @Override public void requestInitialized(ServletRequestEvent sre) { //將所有request請求都攜帶上httpSession HttpSession session = ((HttpServletRequest) sre.getServletRequest()).getSession(); log.info("將所有request請求都攜帶上httpSession {}",session.getId()); } public RequestListener() {} @Override public void requestDestroyed(ServletRequestEvent arg0) {} }
此外在我谷歌的過程中有博客提到需要添加一個WebListener的註解,結果發現基於springboot的話,如果不加也不影響
GitHub項目地址:Demo
以上代碼純屬個人研究,如果有錯誤的地方,各位留言或者發郵件都可以!
Springboot-WebSocket初探-獲取HttpSession問題