標籤:情境 stand 影響 ndt ica 官方 this syn author
換了新工作,第一個任務就是和這個有關,以前沒接觸過,沒辦法,各種度娘、穀哥,大部分都是隻言片語,要麼就是特定的配置環境還不貼配置……,踩坑無數, 遂整理成筆記
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,事實證明思路是正確的,但是當我按照這個思路去百度Google發現擷取的一律都是null,然後我就崩潰了
具體編碼實現
- 只貼核心代碼,完整項目在本文的底部,我放在了github上
-
先上核心的伺服器端訊息處理類
/** * WebSocket主要的訊息類 * @author 侯葉飛 */ //onfigurator = WebsocketConfig.class 該屬性就是我上面提到我們可以自己配置的東西@ServerEndpoint(value = "/api/websocket", configurator = WebsocketConfig.class)@Component@Slf4jpublic 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@Slf4jpublic 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容器中進行管理,相當於註冊監聽吧@Slf4jpublic 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) {} }
-
此外在我Google的過程中有部落格提到需要添加一個WebListener的註解,結果發現基於springboot的話,如果不加也不影響
GitHub項目地址:Demo
以上代碼純屬個人研究,如果有錯誤的地方,各位留言或者發郵件都可以!
Springboot-WebSocket初探-擷取HttpSession問題