開發環境:html
spring3+tomcat7+spring-websocket4 + spring security3前端
運行環境:java
windows、Linuxweb
產品將要發佈的消息或其餘須要讓客戶提早知道的消息,在客戶端和服務端創建鏈接後推送spring
給客戶端。windows
WebSocket協議是基於TCP的一種新的網絡協議。後端
一、WebSocket是HTML5出的東西(協議),跟HTTP協議基本沒有關係,只是爲了兼容現有瀏覽器的握手規範而已瀏覽器
二、HTTP有1.1和1.0之說,也就是所謂的keep-alive,把多個HTTP請求合併爲一個tomcat
三、Websocket是一個持久化的協議,相對於HTTP這種非持久的協議來講安全
四、典型的Websocket握手
Chrome | Supported in version 4+ |
Firefox | Supported in version 4+ |
Opera | Supported in version 10+ |
Safari | Supported in version 5+ |
IE(Internet Explorer) | Supported in version 10+ |
一、長鏈接,實現雙向通訊, 具備底層socket的特色,實現真正意義上的推送功能。
二、是HTML5的技術之一,具備巨大的應用前景
三、節約帶寬,節省服務器資源
缺點:
少部分瀏覽器不支持,瀏覽器支持的程度與方式有區別
前端代碼:
1 var DJCW_webSocket = (function(){ 2 var webSocket = null, 3 tryTime = 0, 4 initSocket; 5 6 initSocket = function(){ 7 debugger 8 var _marquee = "<marquee behavior='scroll' direction='left' behavior='scroll'>"; 9 10 if (!window.WebSocket) { 11 alert("您的瀏覽器不支持websocket!"); 12 return false; 13 } 14 15 webSocket = new WebSocket('ws://127.0.0.1:8082/projectname/websocket'); 16 17 // 收到服務端消息 18 webSocket.onmessage = function(msg) { 19 DJCW.messagesScroll(msg.data); 20 }; 21 22 // 異常 23 webSocket.onerror = function(event) {}; 24 25 // 創建鏈接 26 webSocket.onopen = function(event) {}; 27 28 // 斷線重連 29 webSocket.onclose = function() { 30 // 重試10次,每次之間間隔10秒 31 if (tryTime < 10) { 32 setTimeout(function() { 33 webSocket = null; 34 tryTime++; 35 initSocket(); 36 }, 500); 37 } else { 38 tryTime = 0; 39 } 40 }; 41 }; 42 43 initModule = function() { 44 initSocket(); 45 }; 46 return { 47 initSocket:initSocket, 48 initModule : initModule 49 }; 50 })(); 51 52 $(function() { 53 DJCW_webSocket.initModule(); 54 window.onbeforeunload = function() { 55 // 離開頁面時的其餘操做 56 }; 57 });
後端代碼:
1 import java.io.IOException; 2 import java.util.concurrent.CopyOnWriteArraySet; 3 4 import javax.websocket.OnClose; 5 import javax.websocket.OnError; 6 import javax.websocket.OnMessage; 7 import javax.websocket.OnOpen; 8 import javax.websocket.Session; 9 import javax.websocket.server.ServerEndpoint; 10 11 12 import org.springframework.stereotype.Service; 13 14 /** 15 * 功能說明:websocket處理類, 使用J2EE7的標準 切忌直接在該鏈接處理類中加入業務處理代碼 做者:ydd(2017-04-12 15:29) 16 */ 17 // relationId和userCode是個人業務標識參數,websocket是鏈接的路徑,能夠自行定義 18 @ServerEndpoint("/websocket") 19 @Service 20 public class WebsocketEndPoint { 21 22 private static final Log LOG = Log.getLogger(WebsocketEndPoint.class); 23 24 private static CopyOnWriteArraySet<WebsocketEndPoint> sessions = new CopyOnWriteArraySet<WebsocketEndPoint>(); 25 private Session session; 26 27 28 public WebsocketEndPoint() { 29 } 30 31 /** 32 * @Title: onOpen 33 * @Description: (打開鏈接時觸發) 34 * @param @param session 設定文件 35 * @return void 返回類型 36 * @throws 37 */ 38 @OnOpen 39 public void onOpen(Session session) { 40 this.session = session; 41 sessions.add(this); 42 } 43 44 45 /** 46 * @Title: onMessage 47 * @Description: (收到客戶端消息時觸發) 48 * @param @param message 49 * @param @return 設定文件 50 * @return String 返回類型 51 * @throws 52 */ 53 @OnMessage 54 public String onMessage(String message) { 55 return "Got your message (" + message + ").Thanks !"; 56 } 57 58 /** 59 * @Title: onError 60 * @Description: (異常時觸發) 61 * @param @param throwable 62 * @param @param session 設定文件 63 * @return void 返回類型 64 * @throws 65 */ 66 @OnError 67 public void onError(Throwable throwable, Session session) { 68 LOG.e("Chat Error: " + throwable.toString()); 69 } 70 71 /** 72 * @Title: onClose 73 * @Description: (關閉鏈接時觸發) 74 * @param @param session 設定文件 75 * @return void 返回類型 76 * @throws 77 */ 78 @OnClose 79 public void onClose(Session session) { 80 sessions.remove(this); 81 } 82 83 /** 84 * 向全部用戶發送消息 85 * 86 * @param msg 87 */ 88 public void sendUser(String msg) { 89 try { 90 if (sessions.size() != 0) { 91 for (WebsocketEndPoint s : sessions) { 92 if (s != null) { 93 s.session.getBasicRemote().sendText(msg); 94 } 95 } 96 } 97 } catch (IOException e) { 98 e.printStackTrace(); 99 } 100 } 101 102 }
在線聊天室、在線客服系統、評論系統、WebIM等
WebSocket和傳統的HTTP交互方式的區別以下圖:
1 <!-- spring security --> 2 <filter> 3 <filter-name>springSecurityFilterChain</filter-name> 4 <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> 5 </filter> 6 <!-- 因爲加入了websocket,不能寫成/*,否則會出現302循環重定向,由於和 DispatcherServlet相沖突--> 7 <filter-mapping> 8 <filter-name>springSecurityFilterChain</filter-name> 9 <url-pattern>*.js</url-pattern> 10 <url-pattern>*.jsp</url-pattern> 11 <!--經過SecurityContextHolder.getContext().getAuthentication().getPrincipal()能夠獲取到用戶名 --> 12 <url-pattern>/login/</url-pattern> 13 <!-- 登陸時springsecurity自帶的類進行用戶密碼認證以及賦予相關權限 --> 14 <url-pattern>/j_spring_security_check</url-pattern> 15 <!-- 退出時經過springsecurity自帶的url進行退出並清除session --> 16 <url-pattern>/j_spring_security_logout</url-pattern> 17 </filter-mapping>
一、在同一瀏覽器同時打開同一產品帶有ws協議請求的多個標籤頁,是否都會走onClose方法(即刪除相應的session)
經測試是會的
二、static CopyOnWriteArraySet<WebsocketEndPoint> sessions = new CopyOnWriteArraySet<WebsocketEndPoint>();
每次加載類的時候不會new出一個新的實例嗎,這樣的話怎麼實現的將消息推送給全部用戶呢
不會每次都new出一個新的實例
static變量
按照是否靜態的對類成員變量進行分類可分兩種:一種是被static修飾的變量,叫靜態變量或類變量;另外一種是沒有被static修飾的變量,叫實例變量。
二者的區別是:
對於靜態變量在內存中只有一個拷貝(節省內存),JVM只爲靜態分配一次內存,在加載類的過程當中完成靜態變量的內存分配,可用類名直接訪問(方便),
固然也能夠經過對象來訪問(可是這是不推薦的)。
對於實例變量,沒建立一個實例,就會爲實例變量分配一次內存,實例變量能夠在內存中有多個拷貝,互不影響(靈活)。
CopyOnWriteArraySet介紹:
屬於java.util.concurrent包下的,java.util.concurrent 包添加了多個新的線程安全集合類(ConcurrentHashMap、CopyOnWriteArrayList 和
CopyOnWriteArraySet)這些類的目的是提供高性能、高度可伸縮性、線程安全的基本集合類型版本,經過同步的封裝工廠(Collections.synchronizedMap()、
synchronizedList() 和 synchronizedSet()),非線程安全集合都可表現爲線程安全的