官术网_书友最值得收藏!

二、web端的消息推送

這一章節主要對幾種消息推送的方式進行原理性的講解,并貼出簡單實現的代碼。

主要介紹其中的五種實現方式:短輪詢、CometFlash XMLSocketServer-sentWebSocket

短輪詢

指在特定的的時間間隔(如每10秒),由瀏覽器對服務器發出HTTP request,然后由服務器返回最新的數據給客戶端的瀏覽器。瀏覽器做處理后進行顯示。無論后端此時是否有新的消息產生,都會進行響應。字面上看,這種方式是最簡單的。這種方式的優點是,后端編寫非常簡單,邏輯不復雜。但是缺點是請求中大部分中是無用的,浪費了帶寬和服務器資源。總結來說,簡單粗暴,適用于小型(偷懶)應用。

基于Jqueryajax前端代碼:


<body>

<div id="push"></div>

<script>

$(function () {

setInterval(function () {

getMsg(function (res) {

$("#push").append("<p>" + res +"</p>");

})

},10000);

});

function getMsg(handler){

$.ajax({

url:"/ShortPollingServlet",

type:"post",

success:function (res) {

handler(res)

}

});

}

</script>

</body>

servlet簡單實現后端代碼:


public class ShortPollingServlet extends HttpServlet {

public static int count = 0;

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

//模擬業務代碼

count++;

response.getWriter().print("msg:" + count );

}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

doPost(request,response);

}

}

Comet

包括了長輪詢和長連接,長輪詢是客戶端向服務器發送Ajax請求,服務器接到請求后hold住連接,直到有新消息才返回響應信息并關閉連接,客戶端處理完響應信息后再向服務器發送新的請求;長連接是在頁面中的iframe發送請求到服務端,服務端hold住請求并不斷將需要返回前端的數據封裝成調用javascript函數的形式響應到前端,前端不斷收到響應并處理。Comet的實現原理和短輪詢相比,很明顯少了很多無用請求,減少了帶寬壓力,實現起來比短輪詢復雜一丟丟。比用短輪詢的同學有夢想時,就可以用Comet來實現自己的推送。

長輪詢的優點很明顯,在無消息的情況下不會頻繁的請求,耗費資小并且實現了服務端主動向前端推送的功能,但是服務器hold連接會消耗資源,返回數據順序無保證,難于管理維護。WebQQ(好像掛了)就是這樣實現的。

基于Jqueryajax前端代碼:


<body>

<div id="push"></div>

<script>

$(function () {

getMsg();

});

function getMsg() {

$.ajax({

url:"/LongPollingServlet",

type:"post",

success:function (res) {

$("#push").append("<p>" + res +"</p>");

getMsg();

}

});

}

</script>

</body>

servlet簡單實現后端代碼:


public class LongPollingServlet extends HttpServlet {

public static int count = 0;

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

count++;

//睡眠時間模擬業務操作等待時間

double random = Math.round(Math.random()*10);

long sleepTime = new Double(random).longValue();

try{

Thread.sleep(sleepTime*1000);

response.getWriter().print("msg:" + count + " after " + sleepTime + "seconds servicing");

}catch (Exception e){

e.printStackTrace();

}

}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

doPost(request,response);

}

}

長連接優點是消息即是到達,不發無用請求,管理起來也相對方便。缺點是服務端維護一個長連接會增加開銷。比如Gmail聊天(沒用過)就是這樣實現的。

基于Jqueryajax前端代碼:


<head>

<title>pushPage</title>

<script type="text/javascript">

function loadData(msg) {

var newChild = document.createElement("p");

newChild.innerHTML = msg;

document.getElementById("push").appendChild(newChild);

}

</script>

</head>

<body>

<div id="push"></div>

<iframe src="/LongConnServlet" frameborder="0" name="longConn"></iframe>

</body>

servlet簡單實現后端代碼:


public class LongConnServlet extends HttpServlet {

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

boolean flag = true;

int i = 0;

while (flag){

try {

//模擬每1秒查詢一次數據庫,看是否有新的消息可以推送

Thread.sleep(1*1000);

}catch (Exception e){

e.printStackTrace();

}

String pushMsg = "push msg : " + i;

response.setContentType("text/html;charset=GBK");

response.getWriter().write("<script type='text/javascript'>parent.loadData('" + pushMsg + "')</script>");

response.flushBuffer();

i++;

if(i==5){

flag = false;

}

}

}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

doPost(request,response);

}

}

Flash XMLSocket

HTML頁面中內嵌入一個使用了XMLSocket類的Flash程序。JavaScript通過調用此Flash程序提供的socket接口與服務器端的socket進行通信。JavaScript在收到服務器端以XML格式傳送的信息后可以很容易地控制HTML頁面的內容顯示。

原理示意圖1

利用Flash XML Socket實現”服務器推”技術前提:

1Flash提供了XMLSocket類,服務器利用SocketFlash發送數據;

2JavaScriptFlash的緊密結合JavaScriptFlash可以相互調用。

優點是實現了socket通信,不再利用無狀態的http進行偽推送。但是缺點更明顯:1.客戶端必須安裝Flash播放器;2.因為 XMLSocket沒有HTTP隧道功能,XMLSocket類不能自動穿過防火墻;3.因為是使用套接口,需要設置一個通信端口,防火墻、代理服務器也可能對非HTTP通道端口進行限制;

這種方案在一些網絡聊天室,網絡互動游戲中已得到廣泛使用。不進行代碼示例。2

Server-sent

服務器推指的是HTML5規范中提供的服務端事件EventSource,瀏覽器在實現了該規范的前提下創建一個EventSource連接后,便可收到服務端的發送的消息,實現一個單向通信。客戶端進行監聽,并對響應的信息處理顯示。該種方式已經實現了服務端主動推送至前端的功能。優點是在單項傳輸數據的場景中完全滿足需求,開發人員擴展起來基本不需要改后端代碼,直接用現有框架和技術就可以集成。

基于HTML5Server-sent事件:


<head>

<title>Title</title>

<script>

var source = new EventSource("/ServerSentServlet");//創建一個新的 EventSource對象,

source.onmessage = function (evt) {//每接收到一次更新,就會發生 onmessage事件

var newChild = document.createElement("p");

newChild.innerHTML = evt.data;

document.getElementById("push").appendChild(newChild);

}

</script>

</head>

<body>

<div id="push"></div>

</body>

servlet簡單實現后端代碼:


public class ServerSentServlet extends HttpServlet {

public static int count = 0;

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

count++;

response.setCharacterEncoding("UTF-8");

response.setHeader("Content-Type", "text/event-stream");//設置服務器端事件流

response.setHeader("Cache-Control","no-cache");//規定不對頁面進行緩存

response.setHeader("Pragma","no-cache");

response.setDateHeader("Expires",0);

PrintWriter pw = response.getWriter();

pw.println("retry: 5000"); //設置請求間隔時間

pw.println("data: " + "msg:" + count +"\n\n");

}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

doPost(request,response);

}

}

WebSocket

WebSocketHTML5下一種新的協議,是基于TCP的應用層協議,只需要一次連接,便可以實現全雙工通信,客戶端和服務端可以相互主動發送消息。客戶端進行監聽,并對響應的消息處理顯示。這個技術相信基本都聽說過,就算沒寫過代碼,也大概知道干嘛的。通過名字就能知道,這是一個Socket連接,一個能在瀏覽器上用的Socket連接。是HTML5標準中的一個內容,瀏覽器通過javascript腳本手動創建一個TCP連接與服務端進行通訊。優點是雙向通信,都可以主動發送消息,既可以滿足“問”+“答”的響應機制,也可以實現主動推送的功能。缺點就是編碼相對來說會多點,服務端處理更復雜(我覺得當一條有情懷的咸魚就應該用這個!)。

前端代碼:


<body>

<div id="push"></div>

</body>

<script>

$(function () {

var webSocket = new WebSocket("ws://localhost:8080/ws");

webSocket.onmessage = function (ev) {

$("#push").append("<p>" + ev.data +"</p>");

}

})

</script>

基于注解簡單實現后端代碼:


@ServerEndpoint("/ws")

public class MyWebSocket {

private Session session;

public MyWebSocket() {

}

@OnOpen

public void onOpen(Session session) {

this.session = session;

System.out.println("someone connect");

int count = 1;

while (count<=5){

//睡眠時間模擬業務操作等待時間

double random = Math.round(Math.random()*10);

long sleepTime = new Double(random).longValue();

try {

Thread.sleep(sleepTime*1000);

session.getBasicRemote().sendText("msg:" + count +" from server after" + sleepTime + " seconds");

}catch (Exception e){

e.printStackTrace();

}

count++;

}

}

@OnError

public void onError(Throwable t){

System.out.println("something error");

}

}

以上是對五種推送方式原理的簡單講解和代碼的實現。

主站蜘蛛池模板: 聊城市| 齐齐哈尔市| 碌曲县| 彭州市| 合阳县| 噶尔县| 大英县| 定结县| 新野县| 五常市| 虞城县| 敖汉旗| 酒泉市| 榕江县| 莲花县| 和顺县| 浦北县| 洛阳市| 无为县| 娱乐| 南漳县| 东光县| 汕尾市| 清涧县| 吴川市| 双城市| 无锡市| 钟祥市| 汕头市| 青海省| 明光市| 法库县| 亳州市| 邵阳县| 城口县| 石棉县| 斗六市| 修文县| 永胜县| 定边县| 安平县|