websocket技術分享

開發環境html

spring3+tomcat7+spring-websocket4 + spring security3前端

運行環境java

windows、Linuxweb

 

1、背景:

產品將要發佈的消息或其餘須要讓客戶提早知道的消息,在客戶端和服務端創建鏈接後推送spring

給客戶端。windows

 

2、WebSocket是什麼

WebSocket協議是基於TCP的一種新的網絡協議。後端

 

3、WebSocket身世挖掘

一、WebSocket是HTML5出的東西(協議),跟HTTP協議基本沒有關係,只是爲了兼容現有瀏覽器的握手規範而已瀏覽器

 

二、HTTP有1.1和1.0之說,也就是所謂的keep-alive,把多個HTTP請求合併爲一個tomcat

 

三、Websocket是一個持久化的協議,相對於HTTP這種非持久的協議來講安全

 

四、典型的Websocket握手

Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Version: 13
Origin: http://example.com
 
五、實現了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+

 

 

 

 

 

 

 

 

4、WebSocket的特色

一、長鏈接,實現雙向通訊, 具備底層socket的特色,實現真正意義上的推送功能

二、是HTML5的技術之一,具備巨大的應用前景

三、節約帶寬,節省服務器資源

缺點:

少部分瀏覽器不支持,瀏覽器支持的程度與方式有區別

 

5、WebSocket如何用

前端代碼:

 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 }

 

6、WebSocket應用場景

在線聊天室、在線客服系統、評論系統、WebIM等

 

7、WebSocket的實現原理

在實現websocket連線過程當中,須要經過瀏覽器發出websocket連線請求,而後服務器發出迴應,這個過程一般稱爲「握手」 (handshaking)。

 

在 WebSocket API,瀏覽器和服務器只須要作一個握手的動做,而後,瀏覽器和服務器之間就造成了一條快速通道。二者之間就直接能夠數
據互相傳送。在此WebSocket 協議中,爲咱們實現即時服務帶來了兩大好處:
1. Header
互相溝通的Header是很小的-大概只有 2 Bytes
2. Server Push
服務器的推送,服務器再也不被動的接收到瀏覽器的request以後才返回數據,而是在有新數據時就主動推送給瀏覽器。
 

WebSocket和傳統的HTTP交互方式的區別以下圖:

 

8、遇到的錯誤

 
二、Message will not be sent because the WebSocket session has been closed
服務端不正常關閉後,在後臺的 OnError的方法中拋出的異常,能夠不拋出異常
 
三、spring security3與WebSocket結合使用時報302循環重定向錯誤
緣由
springsecurity的過濾器DelegatingFilterProxy和springMVC的過濾器DispatcherServlet
衝突所致使的。
解決方案(只將spring security的url-pattern中的/*改成具體的對指定的相關請求進行攔截就能夠了)
 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> 

 9、頭腦風暴

一、在同一瀏覽器同時打開同一產品帶有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()),非線程安全集合都可表現爲線程安全的

相關文章
相關標籤/搜索