一、comet簡介:
comet :基於 HTTP長連接的“服務器推”技術,是一種新的 Web 應用架構。基於這種架構開發的應用中,服務器端會主動以異步的方式向客戶端程序推送數據,而不需要客戶端顯式的發出請求。Comet 架構非常適合事件驅動的 Web 應用,以及對交互性和實時性要求很強的應用,如股票交易行情分析、聊天室和 Web 版在線游戲等。
二、comet4j功能特性
- 推送消息廣播。
- 推送定向消息。
- 提供連接上線前、上線、下線前、下線、發送消息等多種可處理事件。
- 消息緩存機制,確保長輪詢工作模式下不丟失消息。
- 客戶端正常下線,服務端可立即感知。
- 客戶端異常停止工作,服務端可定時檢查並感知。
- 以注冊通道應用的方式,讓開發者對框架功能進行擴展,實現自己的應用。
三、comet4j框架特性
- 獨立小巧,不依賴於第三方包。
- 與應用緊密集成,無需獨立應用或服務器。
- 與Session無關的連接機制,為開發人員提供最大程度的功能可控性。
- 面向事件編程,客戶端與服務器端均為事件驅動開發模式,提供了良好的可擴展性機制。
- 各項性能參數均可配置。
- 支持多種主流瀏覽器,並支持Air應用環境。
四、comet4j實戰應用
(1)下載comet4j所需要的jar包和js文件。具體下載地址:http://code.google.com/p/comet4j/
(2)新建web項目:如圖 項目demo下載地址:http://pan.baidu.com/s/1hqsUpzI

(3)在demo中可以發現,index.jsp中所用的CHANNEL必須與Comet4j.java中設置的CHANNEL相一致,在整個推送中,
采用的是單例模式,所以開發人員不必擔心它會消耗大量的內存。
(4)comet4j開發簡單,只需參考其客戶端和服務端的API文檔,做出你想要的推送功能應該是沒有問題的。
第二種:大家比較熟悉
一.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' ~
二、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 三、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對返回的數據進行封裝處理,發送給客戶端。
四、代碼實現以及需求
1、項目需要,定時向所有在線用戶推送一個廣告或是推送一個通知之類的(比如服務器升級,請保存好手頭工作之類的)。: f |, D6 R. _0 ~3 d ' p- `, F0 Y* g4 B. g9 ]# ` 2、相關環境 , Nginx、tomcat7、centos 6.5 7 } m0 R% \$ F' u1 A 3、項目框架,springMvc 4.0.6、layer ! u- Q0 {: B2 u- e. `6 G 4、代碼實現:) W R/ N6 U/ C& L/ t3 R) a& h
WebSocketConfig:
- import websocket.handler.SystemWebSocketHandler;$ `, D+ H. H9 E J7 R
- @Configuration
- @EnableWebMvc
- @EnableWebSocket9 Z9 l6 i" h) g4 G$ S" ^& Q
- public class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer{6 K% ]3 U1 O# C- {% n
- @Override+ J+ `% d% m |3 j6 I7 A+ u' \
- public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
- registry.addHandler(systemWebSocketHandler(),"/webSocketServer");4 H0 k% Y9 o8 `/ L6 y7 W/ B' n
- registry.addHandler(systemWebSocketHandler(),"/sockjs/webSocketServer");
- }
- @Bean' \6 ], m4 }% f' E' U" \
- public WebSocketHandler systemWebSocketHandler(){
- return new SystemWebSocketHandler();: S- |& U# }/ C" G$ v5 a
- }
- }
復制代碼
SystemWebSocketHandler:
- public class SystemWebSocketHandler extends TextWebSocketHandler {
- % }- u" Q6 D# F# P# w; P2 d
- private static final ArrayList<WebSocketSession> users = new ArrayList<WebSocketSession>();;4 ?+ A. V$ Y/ }& h
- x- y) r8 U/ n; M+ Y$ l
- public void afterConnectionEstablished(WebSocketSession session) throws Exception {
- System.out.println("ConnectionEstablished");
- users.add(session);% X7 y- v( \* u! ^
- System.out.println("當前用戶"+users.size());
- }! k5 d' v$ [3 b, u5 l, z
- /**( b# S3 }0 }* ?; x* ^
- * 在UI在用js調用websocket.send()時候,會調用該方法
- * @Author 張志朋7 f& A# K4 g8 X. z- |) S3 K
- * @param session
- * @param message- n) Q; i2 ?5 Y, d4 m
- * @throws Exception
- * @Date 2016年3月4日
- * 更新日志& n. c% Q; Y# ]
- * 2016年3月4日 張志朋 首次創建
- *
- */
- @Override
- protected void handleTextMessage(WebSocketSession session,5 l. }, a5 j4 W8 j, U% x
- TextMessage message) throws Exception {( ~( P! W; T. H$ G* U8 W
- super.handleTextMessage(session, message);
- sendMessageToUsers(session,message);
- }
- @Override9 m3 d( B# t% r" z0 e
- public void handleTransportError(WebSocketSession session, Throwable exception) throws IOException {
- if(session.isOpen()){% x j0 I! c0 S5 Y
- session.close();
- }
- users.remove(session);5 @6 y! F$ k, D- s/ N9 v5 V
- }) m1 |, z8 r$ ]0 B
- @Override
- public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {& u* X6 C/ N* }. ?. e2 w
- users.remove(session);, o, ?0 A. J7 o2 z6 `' b
- }8 p# X, ?0 m7 `+ J
- % V3 r% E, m; }1 ]5 D
- @Override) A1 [; K/ }6 D% [! ^
- public boolean supportsPartialMessages() {& Y' N& U! ~* z( m! h* {. f
- return false;
- } t9 ]* K% z7 C( C# E- |- F+ i
- /**! Z3 L$ S' Q. \1 G- p: I3 {. D1 E9 W
- * 給所有在線用戶發送消息
- * @Author 張志朋
- * @param message void* Y) `$ f0 T4 M+ b6 q8 X2 p5 F
- * @Date 2016年3月4日
- * 更新日志1 q% I4 Y E. @. Z# H- E
- * 2016年3月4日 張志朋 首次創建
- *5 V5 u3 a+ G5 r# P+ f, Y/ {
- */2 H6 z3 A/ m4 ?0 h( ~5 o% X
- public void sendMessageToUsers(WebSocketSession session,TextMessage message) {/ l f: d! @" [% g% ]% j! ?
- for (WebSocketSession user : users) {
- try {; K! [3 k) B! }. v
- if (user.isOpen()) {
- user.sendMessage(message);, ?$ r7 A% ? n1 ]. [7 @$ j
- }
- } catch (IOException e) {
- e.printStackTrace();: c1 t* |1 j$ I% o4 t8 H
- }% ]6 X6 _9 e) z, `) j% `! H2 N
- }
- }2 ^# o( i `( h' Z7 L
- }
復制代碼
信息輸入 index.html:- \0 z& k; w5 d$ U1 ~( ~* T
- <html xmlns="http://www.w3.org/1999/xhtml">
- <head>7 a8 f3 O5 v; b# g3 B: ^
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- <title>請輸入任意消息</title>
- <script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>7 @( l1 e# @) X
- <script src="http://cdn.sockjs.org/sockjs-0.3.min.js"></script>
- <script type="text/javascript">
- var ws = null;
- $(function () {
- if ('WebSocket' in window) {
- ws = new WebSocket('ws://127.0.0.1:8080/webSocketServer'); 5 S+ j {# E8 F2 o( ^2 L8 A: f: F" d
- }
- else if ('MozWebSocket' in window) {$ j1 }4 R% _- _& z3 F
- ws = new MozWebSocket("ws://127.0.0.1:8080/webSocketServer");
- }
- else {
- ws = new SockJS("ws://127.0.0.1:8080/webSocketServer");+ P& w: ?* O' _/ [6 e' t, {0 F
- }. v- m! b& `; f0 P3 C5 t
- ws.onopen = function () {/ B$ e Q$ b) D
- / K$ l6 b6 S7 m2 j' A; m/ j
- };
- ws.onmessage = function (event) {3 O! h. f! h$ m7 Q* M: s2 Y
- " V1 p. q# `/ K
- };
- ws.onclose = function (event) {
- 6 D7 _" h$ ?, Y
- };! e8 }; o, ^! }( o, Q2 r6 \+ z. o( M
- });
- function stop(){+ x8 U6 s7 p. c( U; G0 i
- var message = $("#message").val();$ w. u: E" p5 `! I
- ws.send(message);6 G u8 P3 a( O |5 W8 g8 [
- }
- </script>; u% \/ M8 b7 O0 D5 t5 h" _
- </head>9 U H/ y, ^' M- f# N
- <body class="keBody"> X0 D& H9 u5 d( e0 C: F/ `: R) Q
- 請輸入提示信息: <textarea id="message"></textarea><br />
- <input type="button" value="開始" />
- </body>5 ?% i; h6 ?2 b4 M. [/ f/ b+ j
- </html>
復制代碼
6 q0 F$ y& l t8 y" a( U( I& n webSocket.js 用於導入項目。
- document.write("<script language=javascript src='http://127.0.0.1:8080/js/jquery-1.10.2.min.js'></script>");
- document.write("<script language=javascript src='http://127.0.0.1:8080/layer/layer.js'></script>");; Z" F* y3 X" ^* A6 {! Q9 @( M
- document.write("<script language=javascript src='http://cdn.sockjs.org/sockjs-0.3.min.js'></script>");
- var ws = null;0 K' _) I, _( z: |( t$ b. g
- var basePath = "ws://127.0.0.1:8080/";9 i2 {9 p1 H' T9 b' U
- if ('WebSocket' in window) {# w8 N+ p" v, F/ v% K
- ws = new WebSocket(basePath+'webSocketServer'); 4 q. Z! u% i( I' }. t) o1 |
- }
- else if ('MozWebSocket' in window) {
- ws = new MozWebSocket(basePath+"webSocketServer");0 }9 y3 Y9 T% m3 ?/ [; b; Y7 p
- }
- else {, p. l% Z1 e6 t/ D. m8 q
- ws = new SockJS(basePath+"sockjs/webSocketServer");
- }" C8 y7 f/ C. }# [& W# G
- ws.onopen = function () {
- + J/ T- r4 L" d) [' b9 L2 T
- };3 R6 @8 v! b1 K# O
- ws.onmessage = function (event) {
- pop(event.data);- C6 d! {5 ^1 q5 a
- };
- ws.onclose = function (event) {
- ws.close();1 \8 x$ [- o2 \/ Z
- };
- //提示信息
- function pop(message){. ] Q% }1 `; B
- layer.alert(message);* d4 H2 b0 }) I( j, ~, k
- }
復制代碼
9 \; ^. D: M; Y% o5 B2 [ 5、在項目頭部引入% 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> |