Java Websocket例項【專案實戰系列】
現很多網站為了實現即時通訊,所用的技術都是輪詢(polling)。輪詢是在特定的的時間間隔(如每1秒),由瀏覽器對伺服器發
出HTTP request,然後由伺服器返回最新的資料給客服端的瀏覽器。這種傳統的HTTP request 的模式帶來很明顯的缺點 – 瀏
覽器需要不斷的向伺服器發出請求,然而HTTP request 的header是非常長的,裡面包含的資料可能只是一個很小的值,這樣會佔
用很多的頻寬。WebSocket提供了一個受歡迎的技術,以替代我們過去幾年一直在用的Ajax技術。
一、什麼是WebSocket API?
WebSocket API是下一代客戶端-伺服器的非同步通訊方法。該通訊取代了單個的
TCP套接字,使用ws或wss協議,可用於任意的客戶端和伺服器程式。WebSocket目前由W3C進行標準化。WebSocket已經受到Firefox 4、Chrome 4、Opera 10.70以及Safari 5等
瀏覽器的支援。
WebSocket API最偉大之處在於伺服器和客戶端可以在給定的時間範圍內的任意時刻,相互推送資訊。WebSocket並不限於以
Ajax(或XHR)方式通訊,因為Ajax技術需要客戶端發起請求,而WebSocket伺服器和客戶端可以彼此相互推送資訊;XHR受到域
的限制,而WebSocket允許跨域通訊。
Ajax技術很聰明的一點是沒有設計要使用的方式。WebSocket
為指定目標建立,用於雙向推送訊息。那麼我就開始我在專案中對websocket的使用,首先使用的是J2EE7的架包。
架包加完之後,只需要再新增2個類就可以實現使用的功能了。
首先新增一個Java類,WebsocketController.java
package com.lwl.activemq.controller.websocket; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 功能說明:websocket處理類, 使用J2EE7的標準 * @author Administrator * @create 2016-8-11 下午4:08:35 * @version 1.0 */ @ServerEndpoint("/websocket/{myWebsocket}") public class WebsocketController { private static final Logger logger = LoggerFactory.getLogger(WebsocketController.class); public static Map<String, Session> clients = new ConcurrentHashMap<String, Session>(); /** * 開啟連線時觸發 * @param myWebsocket * @param session */ @OnOpen public void onOpen(@PathParam("myWebsocket") String myWebsocket, Session session){ logger.info("Websocket Start Connecting:" + myWebsocket); System.out.println("進入:"+myWebsocket); clients.put(myWebsocket, session); } /** * 收到客戶端訊息時觸發 * @param myWebsocket * @param message * @return */ @OnMessage public String onMessage(@PathParam("myWebsocket") String myWebsocket, String message) { return "Got your message ("+ message +").Thanks !"; } /** * 異常時觸發 * @param myWebsocket * @param throwable */ @OnError public void onError(@PathParam("myWebsocket") String myWebsocket, Throwable throwable) { logger.info("Websocket Connection Exception:" + myWebsocket); logger.info(throwable.getMessage(), throwable); clients.remove(myWebsocket); } /** * 關閉連線時觸發 * @param myWebsocket */ @OnClose public void onClose(@PathParam("myWebsocket") String myWebsocket) { logger.info("Websocket Close Connection:" + myWebsocket); clients.remove(myWebsocket); } /** * 將資料傳回客戶端 * 非同步的方式 * @param myWebsocket * @param message */ public static void broadcast(String myWebsocket, String message) { if (clients.containsKey(myWebsocket)) { clients.get(myWebsocket).getAsyncRemote().sendText(message); } else { throw new NullPointerException("[" + myWebsocket +"]Connection does not exist"); } } }
然後新增相應的接受頁面index.html:<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <script type="text/javascript" src="resources/jquery-1.8.3.min.js"></script> </head> <body> <script type="text/javascript"> //測試controller是否可以進入 // ajaxDo("/activemq-client/index",null); // function ajaxDo(url,data){ // $.ajax({ // url:url , // type: "post", // dataType: "json", // data: data, // success:function(result){ // if(result.success){ // alert(result.data); // }else{ // alert(result.msg); // } // } // }); // } //--------------------------------- webSocket ---------------------------------------------- initSocket("user"); initSocket("news"); initSocket("client"); function initSocket(myWebsocket) { var webSocket = null; window.onbeforeunload = function () { //離開頁面時的其他操作 }; if (!window.WebSocket) { console("您的瀏覽器不支援websocket!"); return false; } var target = 'ws://' + window.location.host + "/activemq-client/websocket/"+myWebsocket; if ('WebSocket' in window) { webSocket = new WebSocket(target); } else if ('MozWebSocket' in window) { webSocket = new MozWebSocket(target); } else { alert('WebSocket is not supported by this browser.'); return; } // 收到服務端訊息 webSocket.onmessage = function (msg) { alert(msg.data); // 關閉連線 webSocket.onclose(); console.log(msg); }; // 異常 webSocket.onerror = function (event) { console.log(event); }; // 建立連線 webSocket.onopen = function (event) { console.log(event); }; // 斷線 webSocket.onclose = function () { console.log("websocket斷開連線"); }; } </script> </body> </html>
好了,websocket已經實現了,現在最重要的是我們要在哪兒監聽哪些變動,在推送給前端的問題了,這裡我監聽了MQ訊息隊列中的某個Queen,如果獲取到訊息就推送給前端展示,稍後我會把MQ的訊息佇列也寫給大家看,做個參考。(當然你也可以
監聽屬於你自己的物件,主要是呼叫 WebsocketController.broadcast("client", jsonStr);第一個引數和前端的引數一
致,第二個引數是你想推送給前端的內容)。
<span style="font-size:14px;"><span style="font-family:宋體;">package com.lwl.activemq.listener; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.TextMessage; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; import com.alibaba.fastjson.JSON; import com.lwl.activemq.domain.Client; import com.lwl.activemq.controller.websocket.WebsocketController; @Component("clientPushListener") public class ClientPushListener implements MessageListener { protected static final Logger logger = Logger.getLogger(ClientPushListener.class); @Override public void onMessage(Message message) { logger.info("[ClientPushListener.onMessage]:begin onMessage."); TextMessage textMessage = (TextMessage) message; try { String jsonStr = textMessage.getText(); logger.info("[ClientPushListener.onMessage]:receive message is,"+ jsonStr); if (jsonStr != null) { Client info = JSON.parseObject(jsonStr, Client.class); System.out.println("==============================接受到的客戶資訊 開始===================================="); System.out.println(info.toString()); System.out.println("==============================接受到的客戶資訊 結束===================================="); WebsocketController.broadcast("client", jsonStr); } } catch (JMSException e) { logger.error("[ClientPushListener.onMessage]:receive message occured an exception",e); } logger.info("[ClientPushListener.onMessage]:end onMessage."); } }</span></span>