您好,登錄后才能下訂單哦!
本篇內容介紹了“普通輪詢Ajax方式怎么實現(xiàn)”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
基于HTTP的長連接,是一種通過長輪詢方式實現(xiàn)"服務器推"的技術,它彌補了HTTP簡單的請求應答模式的不足,極大地增強了程序的實時性和交互性。
一、什么是長連接、長輪詢?
用通俗易懂的話來說,就是客戶端不停的向服務器發(fā)送請求以獲取最新的數(shù)據(jù)信息。這里的“不停”其實是有停止的,只是我們人眼無法分辨是否停止,它只是一種快速的停下然后又立即開始連接而已。
二、長連接、長輪詢的應用場景
長連接、長輪詢一般應用與WebIM、ChatRoom和一些需要及時交互的網(wǎng)站應用中。其真實案例有:WebQQ、Hi網(wǎng)頁版、Facebook IM等。
三、優(yōu)缺點
輪詢:客戶端定時向服務器發(fā)送Ajax請求,服務器接到請求后馬上返回響應信息并關閉連接。
優(yōu)點:后端程序編寫比較容易。
缺點:請求中有大半是無用,浪費帶寬和服務器資源。
實例:適于小型應用。
長輪詢:客戶端向服務器發(fā)送Ajax請求,服務器接到請求后hold住連接,直到有新消息才返回響應信息并關閉連接,客戶端處理完響應信息后再向服務器發(fā)送新的請求。
優(yōu)點:在無消息的情況下不會頻繁的請求,耗費資源小。
缺點:服務器hold連接會消耗資源,返回數(shù)據(jù)順序無保證,難于管理維護。
實例:WebQQ、Hi網(wǎng)頁版、Facebook IM。
長連接:在頁面里嵌入一個隱蔵iframe,將這個隱蔵iframe的src屬性設為對一個長連接的請求或是采用xhr請求,服務器端就能源源不斷地往客戶端輸入數(shù)據(jù)。
優(yōu)點:消息即時到達,不發(fā)無用請求;管理起來也相對方便。
缺點:服務器維護一個長連接會增加開銷。
實例:Gmail聊天
Flash Socket:在頁面中內嵌入一個使用了Socket類的 Flash 程序JavaScript通過調用此Flash程序提供的Socket接口與服務器端的Socket接口進行通信,JavaScript在收到服務器端傳送的信息后控制頁面的顯示。
優(yōu)點:實現(xiàn)真正的即時通信,而不是偽即時。
缺點:客戶端必須安裝Flash插件;非HTTP協(xié)議,無法自動穿越防火墻。
實例:網(wǎng)絡互動游戲。
四、實現(xiàn)原理
所謂長連接,就是要在客戶端與服務器之間創(chuàng)建和保持穩(wěn)定可靠的連接。其實它是一種很早就存在的技術,但是由于瀏覽器技術的發(fā)展比較緩慢,沒有為這種機制的實現(xiàn)提供很好的支持。所以要達到這種效果,需要客戶端和服務器的程序共同配合來完成。通常的做法是,在服務器的程序中加入一個死循環(huán),在循環(huán)中監(jiān)測數(shù)據(jù)的變動。當發(fā)現(xiàn)新數(shù)據(jù)時,立即將其輸出給瀏覽器并斷開連接,瀏覽器在收到數(shù)據(jù)后,再次發(fā)起請求以進入下一個周期,這就是常說的長輪詢(long-polling)方式。
1. 輪詢的建立
建立輪詢的過程很簡單,瀏覽器發(fā)起請求后進入循環(huán)等待狀態(tài),此時由于服務器還未做出應答,所以HTTP也一直處于連接狀態(tài)中。
2. 數(shù)據(jù)的推送
在循環(huán)過程中,服務器程序對數(shù)據(jù)變動進行監(jiān)控,如發(fā)現(xiàn)更新,將該信息輸出給瀏覽器,隨即斷開連接,完成應答過程,實現(xiàn)“服務器推”。
3. 輪詢的終止
輪詢可能在以下3種情況時終止:
3.1. 有新數(shù)據(jù)推送
當循環(huán)過程中服務器向瀏覽器推送信息后,應該主動結束程序運行從而讓連接斷開,這樣瀏覽器才能及時收到數(shù)據(jù)。
3.2. 沒有新數(shù)據(jù)推送
循環(huán)不能一直持續(xù)下去,應該設定一個最長時限,避免WEB服務器超時(Timeout),若一直沒有新信息,服務器應主動向瀏覽器發(fā)送本次輪詢無新信息的正常響應,并斷開連接,這也被稱為“心跳”信息。
3.3. 網(wǎng)絡故障或異常
由于網(wǎng)絡故障等因素造成的請求超時或出錯也可能導致輪詢的意外中斷,此時瀏覽器將收到錯誤信息。
4. 輪詢的重建
瀏覽器收到回復并進行相應處理后,應馬上重新發(fā)起請求,開始一個新的輪詢周期。
五、程序設計
1、普通輪詢 Ajax方式
客戶端代碼片段
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8" isELIgnored="false" %><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="pragma" content="no-cache"><meta http-equiv="cache-control" content="no-cache"><meta http-equiv="author" content="hoojo & http://hoojo.cnblogs.com"><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><%@ include file="/tags/jquery-lib.jsp"%><script type="text/javascript">$(function () {window.setInterval(function () {$.get("${pageContext.request.contextPath}/communication/user/ajax.mvc",{"timed": new Date().getTime()},function (data) {$("#logs").append("[data: " + data + " ]<br/>");});}, 3000);});</script></head><body><div id="logs"></div></body></html>
客戶端實現(xiàn)的就是用一種普通輪詢的結果,比較簡單。利用setInterval不間斷的刷新來獲取服務器的資源,這種方式的優(yōu)點就是簡單、及時。缺點是鏈接多數(shù)是無效重復的;響應的結果沒有順序(因為是異步請求,當發(fā)送的請求沒有返回結果的時候,后面的請求又被發(fā)送。而此時如果后面的請求比前面的請求要先返回結果,那么當前面的請求返回結果數(shù)據(jù)時已經(jīng)是過時無效的數(shù)據(jù)了);請求多,難于維護、浪費服務器和網(wǎng)絡資源。
服務器端代碼
@RequestMapping("/ajax")public void ajax(long timed, HttpServletResponse response) throws Exception {PrintWriter writer = response.getWriter();Random rand = new Random();// 死循環(huán) 查詢有無數(shù)據(jù)變化while (true) {Thread.sleep(300); // 休眠300毫秒,模擬處理業(yè)務等int i = rand.nextInt(100); // 產(chǎn)生一個0-100之間的隨機數(shù)if (i > 20 && i < 56) { // 如果隨機數(shù)在20-56之間就視為有效數(shù)據(jù),模擬數(shù)據(jù)發(fā)生變化long responseTime = System.currentTimeMillis();// 返回數(shù)據(jù)信息,請求時間、返回數(shù)據(jù)時間、耗時writer.print("result: " + i + ", response time: " + responseTime + ", request time: " + timed + ", use time: " + (responseTime - timed));break; // 跳出循環(huán),返回數(shù)據(jù)} else { // 模擬沒有數(shù)據(jù)變化,將休眠 hold住連接Thread.sleep(1300);}}}
服務器端實現(xiàn),這里就模擬下程序監(jiān)控數(shù)據(jù)的變化。上面代碼屬于SpringMVC 中controller中的一個方法,相當于Servlet中的一個doPost/doGet方法。如果沒有程序環(huán)境適應servlet即可,將方法體中的代碼copy到servlet的doGet/doPost中即可。
服務器端在進行長連接的程序設計時,要注意以下幾點:
1. 服務器程序對輪詢的可控性
由于輪詢是用死循環(huán)的方式實現(xiàn)的,所以在算法上要保證程序對何時退出循環(huán)有完全的控制能力,避免進入死循環(huán)而耗盡服務器資源。
2. 合理選擇“心跳”頻率
從圖1可以看出,長連接必須由客戶端不停地進行請求來維持,所以在客戶端和服務器間保持正常的“心跳”至為關鍵,參數(shù)POLLING_LIFE應小于WEB服務器的超時時間,一般建議在10~20秒左右。
3. 網(wǎng)絡因素的影響
在實際應用時,從服務器做出應答,到下一次循環(huán)的建立,是有時間延遲的,延遲時間的長短受網(wǎng)絡傳輸?shù)榷喾N因素影響,在這段時間內,長連接處于暫時斷開的空檔,如果恰好有數(shù)據(jù)在這段時間內發(fā)生變動,服務器是無法立即進行推送的,所以,在算法設計上要注意解決由于延遲可能造成的數(shù)據(jù)丟失問題。
4. 服務器的性能
在長連接應用中,服務器與每個客戶端實例都保持一個持久的連接,這將消耗大量服務器資源,特別是在一些大型應用系統(tǒng)中更是如此,大量并發(fā)的長連接有可能導致新的請求被阻塞甚至系統(tǒng)崩潰,所以,在進行程序設計時應特別注意算法的優(yōu)化和改進,必要時還需要考慮服務器的負載均衡和集群技術。上圖是返回的結果,可以看到先發(fā)出請求,不一定會最先返回結果。這樣就不能保證順序,造成臟數(shù)據(jù)或無用的連接請求。可見對服務器或網(wǎng)絡的資源浪費。
2、普通輪詢 iframe方式
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8" isELIgnored="false" %><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="pragma" content="no-cache"><meta http-equiv="cache-control" content="no-cache"><meta http-equiv="expires" content="0"><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><%@ include file="/tags/jquery-lib.jsp"%><script type="text/javascript">$(function () {window.setInterval(function () {$("#logs").append("[data: " + $($("#frame").get(0).contentDocument).find("body").text() + " ]<br/>");$("#frame").attr("src", "${pageContext.request.contextPath}/communication/user/ajax.mvc?timed=" + new Date().getTime());// 延遲1秒再重新請求window.setTimeout(function () {window.frames["polling"].location.reload();}, 1000);}, 5000);});</script></head><body><iframe id="frame" name="polling" ></iframe><div id="logs"></div></body></html>
這里的客戶端程序是利用隱藏的iframe向服務器端不停的拉取數(shù)據(jù),將iframe獲取后的數(shù)據(jù)填充到頁面中即可。同ajax實現(xiàn)的基本原理一樣,唯一不同的是當一個請求沒有響應返回數(shù)據(jù)的情況下,下一個請求也將開始,這時候前面的請求將被停止。如果要使程序和上面的ajax請求一樣也可以辦到,那就是給每個請求分配一個獨立的iframe即可。下面是返回的結果:
其中紅色是沒有成功返回請求就被停止(后面請求開始)掉的請求,黑色是成功返回數(shù)據(jù)的請求。
3、長連接iframe方式
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8" isELIgnored="false" %><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="pragma" content="no-cache"><meta http-equiv="cache-control" content="no-cache"><meta http-equiv="author" content="hoojo & http://hoojo.cnblogs.com"><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><%@ include file="/tags/jquery-lib.jsp"%><script type="text/javascript">$(function () {window.setInterval(function () {var url = "${pageContext.request.contextPath}/communication/user/ajax.mvc?timed=" + new Date().getTime();var $iframe = $('<iframe id="frame" name="polling" src="' + url + '"></iframe>');$("body").append($iframe);$iframe.load(function () {$("#logs").append("[data: " + $($iframe.get(0).contentDocument).find("body").text() + " ]<br/>");$iframe.remove();});}, 5000);});</script></head><body><div id="logs"></div></body></html>
這個輪詢方式就是把剛才上面的稍微改下,每個請求都有自己獨立的一個iframe,當這個iframe得到響應的數(shù)據(jù)后就把數(shù)據(jù)push到當前頁面上。使用此方法已經(jīng)類似于ajax的異步交互了,這種方法也是不能保證順序的、比較耗費資源、而且總是有一個加載的條在地址欄或狀態(tài)欄附件(當然要解決可以利用htmlfile,Google的攻城師們已經(jīng)做到了,網(wǎng)上也有封裝好的lib庫),但客戶端實現(xiàn)起來比較簡單。
如果要保證有序,可以不使用setInterval,將創(chuàng)建iframe的方法放在load事件中即可,即使用遞歸方式。調整后的代碼片段如下:
<script type="text/javascript">$(function () {(function iframePolling() {var url = "${pageContext.request.contextPath}/communication/user/ajax.mvc?timed=" + new Date().getTime();var $iframe = $('<iframe id="frame" name="polling" src="' + url + '"></iframe>');$("body").append($iframe);$iframe.load(function () {$("#logs").append("[data: " + $($iframe.get(0).contentDocument).find("body").text() + " ]<br/>");$iframe.remove();// 遞歸iframePolling();});})();});</script>
這種方式雖然保證了請求的順序,但是它不會處理請求延時的錯誤或是說很長時間沒有返回結果的請求,它會一直等到返回請求后才能創(chuàng)建下一個iframe請求,總會和服務器保持一個連接。和以上輪詢比較,缺點就是消息不及時,但保證了請求的順序。
4、ajax實現(xiàn)長連接
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8" isELIgnored="false" %><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="pragma" content="no-cache"><meta http-equiv="cache-control" content="no-cache"><meta http-equiv="expires" content="0"><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><%@ include file="/tags/jquery-lib.jsp"%><script type="text/javascript">$(function () {(function longPolling() {$.ajax({url: "${pageContext.request.contextPath}/communication/user/ajax.mvc",data: {"timed": new Date().getTime()},dataType: "text",timeout: 5000,error: function (XMLHttpRequest, textStatus, errorThrown) {$("#state").append("[state: " + textStatus + ", error: " + errorThrown + " ]<br/>");if (textStatus == "timeout") { // 請求超時longPolling(); // 遞歸調用// 其他錯誤,如網(wǎng)絡錯誤等} else {longPolling();}},success: function (data, textStatus) {$("#state").append("[state: " + textStatus + ", data: { " + data + "} ]<br/>");if (textStatus == "success") { // 請求成功longPolling();}}});})();});</script></head><body>
上面這段代碼就是才有Ajax的方式完成長連接,主要優(yōu)點就是和服務器始終保持一個連接。如果當前連接請求成功后,將更新數(shù)據(jù)并且繼續(xù)創(chuàng)建一個新的連接和服務器保持聯(lián)系。如果連接超時或發(fā)生異常,這個時候程序也會創(chuàng)建一個新連接繼續(xù)請求。這樣就大大節(jié)省了服務器和網(wǎng)絡資源,提高了程序的性能,從而也保證了程序的順序。
“普通輪詢Ajax方式怎么實現(xiàn)”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關的知識可以關注億速云網(wǎng)站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內容。