WEB消息推送-comet4j

1、comet簡介:     

comet :基於 HTTP長鏈接的「服務器推」技術,是一種新的 Web 應用架構。基於這種架構開發的應用中,服務器端會主動以異步的方式向客戶端程序推送數據,而不須要客戶端顯式的發出請求。Comet 架構很是適合事件驅動的 Web 應用,以及對交互性和實時性要求很強的應用,如股票交易行情分析、聊天室和 Web 版在線遊戲等。javascript

2、comet4j功能特性

  • 推送消息廣播。
  • 推送定向消息。
  • 提供鏈接上線前、上線、下線前、下線、發送消息等多種可處理事件。
  • 消息緩存機制,確保長輪詢工做模式下不丟失消息。
  • 客戶端正常下線,服務端可當即感知。
  • 客戶端異常中止工做,服務端可定時檢查並感知。
  • 以註冊通道應用的方式,讓開發者對框架功能進行擴展,實現本身的應用。

3、comet4j框架特性

  • 獨立小巧,不依賴於第三方包。
  • 與應用緊密集成,無需獨立應用或服務器。
  • 與Session無關的鏈接機制,爲開發人員提供最大程度的功能可控性。
  • 面向事件編程,客戶端與服務器端均爲事件驅動開發模式,提供了良好的可擴展性機制。
  • 各項性能參數都可配置。
  • 支持多種主流瀏覽器,並支持Air應用環境。

4、comet4j實戰應用

  (1)下載comet4j所須要的jar包和js文件。具體下載地址:http://code.google.com/p/comet4j/html

  (2)新建web項目:如圖 項目demo下載地址:http://pan.baidu.com/s/1hqsUpzIjava

    

   (3)在demo中能夠發現,index.jsp中所用的CHANNEL必須與Comet4j.java中設置的CHANNEL相一致,在整個推送中,jquery

     採用的是單例模式,因此開發人員沒必要擔憂它會消耗大量的內存。web

   (4)comet4j開發簡單,只需參考其客戶端和服務端的API文檔,作出你想要的推送功能應該是沒有問題的。spring

 

  第二種:你們比較熟悉apache

一.WebSocket簡單介紹

 

 

一.WebSocket簡單介紹
  隨着互聯網的發展,傳統的HTTP協議已經很難知足Web應用日益複雜的需求了。近年來,隨着HTML5的誕生,WebSocket協議被提出,它實現了瀏覽器與服務器的全雙工通訊,擴展了瀏覽器與服務端的通訊功能,使服務端也能主動向客戶端發送數據。$ j6 s6 P/ d( K- G' m
  咱們知道,傳統的HTTP協議是無狀態的,每次請求(request)都要由客戶端(如 瀏覽器)主動發起,服務端進行處理後返回response結果,而服務端很難主動向客戶端發送數據;這種客戶端是主動方,服務端是被動方的傳統Web模式 對於信息變化不頻繁的Web應用來講形成的麻煩較小,而對於涉及實時信息的Web應用卻帶來了很大的不便,如帶有即時通訊、實時數據、訂閱推送等功能的應 用。在WebSocket規範提出以前,開發人員若要實現這些實時性較強的功能,常常會使用折衷的解決方法:輪詢(polling)和Comet技術。其實後者本質上也是一種輪詢,只不過有所改進。
  輪詢是最原始的實現實時Web應用的解決方案。輪詢技術要求客戶端以設定的時間間隔週期性地向服務端發送請求,頻繁地查詢是否有新的數據改動。明顯地,這種方法會致使過多沒必要要的請求,浪費流量和服務器資源。
  Comet技術又能夠分爲長輪詢和流技術。長輪詢改進了上述的輪詢技術,減少了無用的請求。它會爲某些數據設定過時時間,當數據過時後纔會向服務端發送請求;這種機制適合數據的改動不是特別頻繁的狀況。流技術一般是指客戶端使用一個隱藏的窗口與服務端創建一個HTTP長鏈接,服務端會不斷更新鏈接狀態以保持HTTP長鏈接存活;這樣的話,服務端就能夠經過這條長鏈接主動將數據發送給客戶端;流技術在大併發環境下,可能會考驗到服務端的性能。
  這兩種技術都是基於請求-應答模式,都不算是真正意義上的實時技術;它們的每一次請求、應答,都浪費了必定流量在相同的頭部信息上,而且開發複雜度也較大。. C  w( i) E4 k. P, m; g
  伴隨着HTML5推出的WebSocket,真正實現了Web的實時通訊,使B/S模式具有了C/S模式的實時通訊能力。WebSocket的工做流程是這 樣的:瀏覽器經過javaScript向服務端發出創建WebSocket鏈接的請求,在WebSocket鏈接創建成功後,客戶端和服務端就能夠經過 TCP鏈接傳輸數據。由於WebSocket鏈接本質上是TCP鏈接,不須要每次傳輸都帶上重複的頭部數據,因此它的數據傳輸量比輪詢和Comet技術小 了不少。本文不詳細地介紹WebSocket規範,主要介紹下WebSocket在Java Web中的實現。# k- L0 {# i. k
  JavaEE 7中出了JSR-356:Java API for WebSocket規範。很多Web容器,如Tomcat,Nginx,Jetty等都支持WebSocket。Tomcat從7.0.27開始支持 WebSocket,從7.0.47開始支持JSR-356,下面的Demo代碼也是須要部署在Tomcat7.0.47以上的版本才能運行。# W" f) D, @: u) Y0 L' ~

2、WebSocket協議介紹
 WebSocket協議是一種雙向通訊協議,它創建在TCP之上,同http同樣經過TCP來傳輸數據,可是它和http最大的不一樣有兩點:1.WebSocket是一種雙向通訊協議,在創建鏈接後,WebSocket服務器和Browser/UA都能主動的向對方發送或接收數據,就像Socket同樣,不一樣的是WebSocket是一種創建在Web基礎上的一種簡單模擬Socket的協議;2.WebSocket須要經過握手鍊接,相似於TCP它也須要客戶端和服務器端進行握手鍊接,鏈接成功後才能相互通訊。簡單的創建握手的時序圖以下:+ h; ~; q4 `/ {
<ignore_js_op> 
握手過程:
Browser與WebSocket服務器經過TCP三次握手創建鏈接,若是這個創建鏈接失敗,那麼後面的過程就不會執行,Web應用程序將收到錯誤消息通知。
在TCP創建鏈接成功後,Browser/UA經過http協議傳送WebSocket支持的版本號,協議的字版本號,原始地址,主機地址等等一些列字段給服務器端。
WebSocket服務器收到Browser/UA發送來的握手請求後,若是數據包數據和格式正確,客戶端和服務器端的協議版本號匹配等等,就接受本次握手鍊接,並給出相應的數據回覆,一樣回覆的數據包也是採用http協議傳輸。- s" j: [- c" o- k
Browser收到服務器回覆的數據包後,若是數據包內容、格式都沒有問題的話,就表示本次鏈接成功,觸發onopen消息,此時Web開發者就能夠在此時經過send接口想服務器發送數據。不然,握手鍊接失敗,Web應用程序會收到onerror消息,而且能知道鏈接失敗的緣由。5 V, k5 ?2 l7 M( \2 b  H
' z7 g9 w, D$ O% k) U
3、Tomcat 7中的Websocket架構3 E7 s, g+ f% r% u4 Y% `
) F' S5 |4 q- W
如圖所示,由於Websocket通訊分爲握手和數據傳輸兩個過程,兩個過程當中須要用到的處理方式是不同的,握手過程是基於HTTP 1.1基礎上的,而數據傳輸是直接基於TCP的流傳輸。 ' ]4 R- C. L! S5 x; y* j
       握手過程當中,在HttpServletRequest的基礎上,封裝了WsHttpServletRequest類,添加了對Request的失效操做函數invalidate()。而在數據通訊時,接受和處理數據過程當中,基於org.apache.coyote.http11.upgrade.UpgradeInbound從新封裝了用於處理數據輸入流的類StreamInbound,並在StreamInbound的基礎上擴展生成了用於消息處理的類MessageInbound。在這兩個數據處理類中均留有onData,onTextData/onBinaryData,onOpen,onClose等事件操做函數接口,這些接口將在載入的代碼類中實現業務邏輯。在用於數據輸出流的類WsOutbound則是封裝了UpgradeOutbound對象實例,基於UpgradeOutbound對象的基礎上,添加了websocket響應有關的處理邏輯。這裏處理函數均爲同步調用的函數,保證websocket響應的時序性。
       Tomcat中Websocket的處理流程以下:<ignore_js_op> 
接收客戶端發來的握手請求,Coyote.http11鏈接器對socket進行解析,造成HttpServletRequest發送給Container。
Container中的相應WebsocketServlet處理請求,如不接受鏈接請求,則返回,如接受鏈接請求,則對請求做出響應,創建起客戶端和服務器的socket鏈接。
服務器此時能夠經過WsOutbound發送數據給客戶端,同時經過StreamInbound監聽socket。/ d3 C$ S' N4 s" m
若是接收到客戶端發來的數據,則將socket數據解析成frame,判斷frame類型,經過事件分發數據到不一樣的邏輯處理流程。
數據返回時調用WsOutbound對返回的數據進行封裝處理,發送給客戶端。

4、代碼實現以及需求

一、項目須要,定時向全部在線用戶推送一個廣告或是推送一個通知之類的(好比服務器升級,請保存好手頭工做之類的)。: f  |, D6 R. _0 ~3 d
' p- `, F0 Y* g4 B. g9 ]# `
二、相關環境 , Nginx、tomcat七、centos 6.5
7 }  m0 R% \$ F' u1 A
三、項目框架,springMvc 4.0.六、layer
! u- Q0 {: B2 u- e. `6 G
四、代碼實現:) W  R/ N6 U/ C& L/ t3 R) a& h

WebSocketConfig:
  1. import websocket.handler.SystemWebSocketHandler;$ `, D+ H. H9 E  J7 R
  2. @Configuration
  3. @EnableWebMvc
  4. @EnableWebSocket9 Z9 l6 i" h) g4 G$ S" ^& Q
  5. public class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer{6 K% ]3 U1 O# C- {% n
  6.     @Override+ J+ `% d% m  |3 j6 I7 A+ u' \
  7.     public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
  8.          registry.addHandler(systemWebSocketHandler(),"/webSocketServer");4 H0 k% Y9 o8 `/ L6 y7 W/ B' n
  9.          registry.addHandler(systemWebSocketHandler(),"/sockjs/webSocketServer");
  10.     }
  11.     @Bean' \6 ], m4 }% f' E' U" \
  12.     public WebSocketHandler systemWebSocketHandler(){
  13.         return new SystemWebSocketHandler();: S- |& U# }/ C" G$ v5 a
  14.     }
  15. }
複製代碼
SystemWebSocketHandler:
  1. public class SystemWebSocketHandler extends  TextWebSocketHandler {
  2. % }- u" Q6 D# F# P# w; P2 d
  3.     private static final ArrayList<WebSocketSession> users = new ArrayList<WebSocketSession>();;4 ?+ A. V$ Y/ }& h
  4.   x- y) r8 U/ n; M+ Y$ l
  5.     public void afterConnectionEstablished(WebSocketSession session) throws Exception {
  6.         System.out.println("ConnectionEstablished");
  7.         users.add(session);% X7 y- v( \* u! ^
  8.         System.out.println("當前用戶"+users.size());
  9.     }! k5 d' v$ [3 b, u5 l, z
  10.     /**( b# S3 }0 }* ?; x* ^
  11.      * 在UI在用js調用websocket.send()時候,會調用該方法
  12.      * @Author    張志朋7 f& A# K4 g8 X. z- |) S3 K
  13.      * @param session
  14.      * @param message- n) Q; i2 ?5 Y, d4 m
  15.      * @throws Exception 
  16.      * @Date    2016年3月4日
  17.      * 更新日誌& n. c% Q; Y# ]
  18.      * 2016年3月4日 張志朋  首次建立
  19.      *
  20.      */
  21.     @Override
  22.     protected void handleTextMessage(WebSocketSession session,5 l. }, a5 j4 W8 j, U% x
  23.             TextMessage message) throws Exception {( ~( P! W; T. H$ G* U8 W
  24.         super.handleTextMessage(session, message);
  25.         sendMessageToUsers(session,message);
  26.     }
  27.     @Override9 m3 d( B# t% r" z0 e
  28.     public void handleTransportError(WebSocketSession session, Throwable exception) throws IOException {
  29.         if(session.isOpen()){% x  j0 I! c0 S5 Y
  30.             session.close();
  31.         }
  32.         users.remove(session);5 @6 y! F$ k, D- s/ N9 v5 V
  33.     }) m1 |, z8 r$ ]0 B
  34.     @Override
  35.     public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {& u* X6 C/ N* }. ?. e2 w
  36.         users.remove(session);, o, ?0 A. J7 o2 z6 `' b
  37.     }8 p# X, ?0 m7 `+ J
  38. % V3 r% E, m; }1 ]5 D
  39.     @Override) A1 [; K/ }6 D% [! ^
  40.     public boolean supportsPartialMessages() {& Y' N& U! ~* z( m! h* {. f
  41.         return false;
  42.     }  t9 ]* K% z7 C( C# E- |- F+ i
  43.     /**! Z3 L$ S' Q. \1 G- p: I3 {. D1 E9 W
  44.      * 給全部在線用戶發送消息
  45.      * @Author    張志朋
  46.      * @param message  void* Y) `$ f0 T4 M+ b6 q8 X2 p5 F
  47.      * @Date    2016年3月4日
  48.      * 更新日誌1 q% I4 Y  E. @. Z# H- E
  49.      * 2016年3月4日 張志朋  首次建立
  50.      *5 V5 u3 a+ G5 r# P+ f, Y/ {
  51.      */2 H6 z3 A/ m4 ?0 h( ~5 o% X
  52.     public void sendMessageToUsers(WebSocketSession session,TextMessage message) {/ l  f: d! @" [% g% ]% j! ?
  53.         for (WebSocketSession user : users) {
  54.             try {; K! [3 k) B! }. v
  55.                 if (user.isOpen()) {
  56.                     user.sendMessage(message);, ?$ r7 A% ?  n1 ]. [7 @$ j
  57.                 }
  58.             } catch (IOException e) {
  59.                 e.printStackTrace();: c1 t* |1 j$ I% o4 t8 H
  60.             }% ]6 X6 _9 e) z, `) j% `! H2 N
  61.         }
  62.     }2 ^# o( i  `( h' Z7 L
  63. }
複製代碼
信息輸入 index.html:- \0 z& k; w5 d$ U1 ~( ~* T
  1. <html xmlns="http://www.w3.org/1999/xhtml">
  2. <head>7 a8 f3 O5 v; b# g3 B: ^
  3. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  4. <title>請輸入任意消息</title>
  5. <script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>7 @( l1 e# @) X
  6. <script src="http://cdn.sockjs.org/sockjs-0.3.min.js"></script>
  7. <script type="text/javascript">
  8. var ws = null;
  9. $(function () {
  10.     if ('WebSocket' in window) {
  11.          ws = new WebSocket('ws://127.0.0.1:8080/webSocketServer'); 5 S+ j  {# E8 F2 o( ^2 L8 A: f: F" d
  12.     } 
  13.     else if ('MozWebSocket' in window) {$ j1 }4 R% _- _& z3 F
  14.         ws = new MozWebSocket("ws://127.0.0.1:8080/webSocketServer");
  15.     } 
  16.     else {
  17.         ws = new SockJS("ws://127.0.0.1:8080/webSocketServer");+ P& w: ?* O' _/ [6 e' t, {0 F
  18.     }. v- m! b& `; f0 P3 C5 t
  19.     ws.onopen = function () {/ B$ e  Q$ b) D
  20. / K$ l6 b6 S7 m2 j' A; m/ j
  21.     };
  22.     ws.onmessage = function (event) {3 O! h. f! h$ m7 Q* M: s2 Y
  23. " V1 p. q# `/ K
  24.     };
  25.     ws.onclose = function (event) {
  26. 6 D7 _" h$ ?, Y
  27.     };! e8 }; o, ^! }( o, Q2 r6 \+ z. o( M
  28. });
  29. function stop(){+ x8 U6 s7 p. c( U; G0 i
  30.     var message = $("#message").val();$ w. u: E" p5 `! I
  31.     ws.send(message);6 G  u8 P3 a( O  |5 W8 g8 [
  32. }
  33. </script>; u% \/ M8 b7 O0 D5 t5 h" _
  34. </head>9 U  H/ y, ^' M- f# N
  35. <body class="keBody">  X0 D& H9 u5 d( e0 C: F/ `: R) Q
  36. 請輸入提示信息: <textarea id="message"></textarea><br />
  37. <input type="button"  value="開始" />
  38. </body>5 ?% i; h6 ?2 b4 M. [/ f/ b+ j
  39. </html>
複製代碼
6 q0 F$ y& l  t8 y" a( U( I& n
webSocket.js  用於導入項目。
  1. document.write("<script language=javascript src='http://127.0.0.1:8080/js/jquery-1.10.2.min.js'></script>");
  2. document.write("<script language=javascript src='http://127.0.0.1:8080/layer/layer.js'></script>");; Z" F* y3 X" ^* A6 {! Q9 @( M
  3. document.write("<script language=javascript src='http://cdn.sockjs.org/sockjs-0.3.min.js'></script>");
  4.     var ws = null;0 K' _) I, _( z: |( t$ b. g
  5.     var basePath = "ws://127.0.0.1:8080/";9 i2 {9 p1 H' T9 b' U
  6.     if ('WebSocket' in window) {# w8 N+ p" v, F/ v% K
  7.          ws = new WebSocket(basePath+'webSocketServer'); 4 q. Z! u% i( I' }. t) o1 |
  8.     } 
  9.     else if ('MozWebSocket' in window) {
  10.         ws = new MozWebSocket(basePath+"webSocketServer");0 }9 y3 Y9 T% m3 ?/ [; b; Y7 p
  11.     } 
  12.     else {, p. l% Z1 e6 t/ D. m8 q
  13.         ws = new SockJS(basePath+"sockjs/webSocketServer");
  14.     }" C8 y7 f/ C. }# [& W# G
  15.     ws.onopen = function () {
  16. + J/ T- r4 L" d) [' b9 L2 T
  17.     };3 R6 @8 v! b1 K# O
  18.     ws.onmessage = function (event) {
  19.         pop(event.data);- C6 d! {5 ^1 q5 a
  20.     };
  21.     ws.onclose = function (event) {
  22.          ws.close();1 \8 x$ [- o2 \/ Z
  23.     };
  24. //提示信息
  25. function pop(message){. ]  Q% }1 `; B
  26.     layer.alert(message);* d4 H2 b0 }) I( j, ~, k
  27. }
複製代碼
9 \; ^. D: M; Y% o5 B2 [
五、在項目頭部引入% D0 _; w8 y) {( C: n6 e$ K
<script language=javascript src='http://127.0.0.1:8080/webSocket.js '></script>


這時查看後臺 會有如下信息 說明 引入成功。# T; \, y% L  c+ d1 ~% U
<ignore_js_op> 

而後在打開頁面 index.html 輸入如下內容 點擊開始便可。/ d% h3 B% d: \9 E
<ignore_js_op> 

若是在網站出現一下提示說明配置成功,這時候全部網站登陸用戶均可以收到此信息。, N5 ^, y) H4 C2 ]3 c* }
<ignore_js_op>
相關文章
相關標籤/搜索