Comet入門及最簡單的Java Demo,cometjavademo
在瀏覽網頁的時候,如果有新的訊息,如何接收到?HTTP協議不能由伺服器主動給用戶端發送訊息。
1、刷微博、逛論壇貼吧,想看最新的資訊怎麼辦?F5重新整理一下就OK了!
2、上面一種方式是被動的,如果使用者不去重新整理頁面,就看不到訊息。如何讓伺服器主動推送訊息給用戶端瀏覽器,一種方式是用setInterval來Ajax定時重新整理。
這樣一來,重新整理操作不是由使用者來操作,而是由瀏覽器去自動發起請求,使用者無法察覺到,給使用者的感覺就是伺服器主動推送訊息給使用者。
實際上這種方式很簡單也很實用,伺服器不用對這種請求做特殊的處理(Comet方式就需要伺服器特殊處理了),所以也經常被使用。
但是對於某些訊息通知,即時性很重要。比如遊戲,一秒鐘能砍出好幾刀甚至直接被帶走了。再比如星爺《少林足球》中,人家一秒鐘幾十萬上下。
這種方式的訊息推送,服務端有訊息並不能立即通知給用戶端,必須要等到Ajax下一次請求才能返回給用戶端。那麼問題來了:兩次請求的間隔時間設成多少合適。設定的間隔時間太短,老是發出HTTP請求占用戶端和伺服器網速和效能,間隔時間長了,用戶端訊息通知不及時。
而對於一些網站的訊息通知,人家可能一兩天才能收到一個訊息,那麼後台一直定時在重新整理的話也是白刷,浪費資源。
3、遊戲、聊天室、股票等經常用到伺服器推送的應用中,通常才會用Comet。Comet實際上是一種HTTP長串連,這點與普通的HTTP有點不一樣,普通的HTTP串連是用戶端發送一個請求,伺服器立馬給出響應,然後串連立即關閉。而Comet是用戶端發出HTTP請求,伺服器不會立馬給出響應,而是阻塞住,直到有訊息需要發送給用戶端的時候才給出響應。
伺服器沒有立即返迴響應,這種情況一般會發生在網速不好或者伺服器效能差的情況下,也就是瀏覽器開啟一個網頁會轉圈圈轉很久。Comet和這個類似,但是它不是網速不好或者伺服器效能差引起的,而是伺服器故意去等待的。
Comet有以下幾種實現方式:
1、長輪詢(long-polling)
用JS發送Ajax請求,但是伺服器不會立馬響應,直到伺服器有訊息給用戶端時再響應,響應完成後串連關閉,用戶端立即再次發送請求,等待響應。
下面是個DEMO:
前端頁面:
<!DOCTYPE HTML><html><head><meta http-equiv="content-type" content="text/html;charset=utf-8"><script type="text/javascript">function getMsg() {// 請求地址是CometServlet對應的URL,加上一個隨機值參數防止緩衝問題var url = "comet" + "?t="+Math.random();var request = new XMLHttpRequest();request.onreadystatechange = function() {if (request.readyState == 4) {if (request.status == 200) {if (request.responseText) {// 追加到HTML中document.body.innerHTML += request.responseText;}}// Ajax完成後,再次Ajax請求getMsg();}};request.open("GET", url, true);request.send();}window.onload = getMsg;</script></head><body></body></html>後台Servlet:
public class CometServlet extends HttpServlet {@Overridepublic void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {// 這裡用Thread.sleep來類比comet,相當於每隔5秒伺服器向用戶端推送一條訊息try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}PrintWriter out = response.getWriter();out.println("helloworld<br>");}}在瀏覽器中開啟html頁面,可以看到每隔5秒伺服器向用戶端推送一個helloworld字串。而接收到字串HTTP串連就斷開了,然後Ajax再立即發出請求等待伺服器的下次響應。
2、流(streaming)
和上面long-polling不同的是,伺服器發完訊息後,不會關閉串連,而是保持HTTP串連繼續等待下一條訊息。這樣用戶端不用每次收到訊息串連關閉後再次請求。
這種方式的原理是HTTP協議回應標頭的一個Header:Transfer-Encoding: chunked。普通的HTTP回應標頭中會有Content-Length表示整個響應的位元組大小,瀏覽器接收到所有的響應資料才會載入內容。而設定Transfer-Encoding: chunked後表示響應大小不固定,瀏覽器接收到一點響應資料就載入一點。
基於流的Comet後台實現是一樣的,但是前端會有幾種不同的方式:
2.1、iframe流
基於streaming的Comet的一種前端實現方式是在HTML中加入一個隱藏的iframe,iframe的src設定成CometServlet的地址。而伺服器有新訊息的話就寫一個script標籤到這個iframe中,包含一段Javascript,瀏覽器載入完成這段JS就會運行JS指令碼,通過JS代碼來控制添加HTML。
前端頁面:
<!DOCTYPE HTML><html><head><meta http-equiv="content-type" content="text/html;charset=utf-8"><script type="text/javascript">// 向HTML追加message,這個函數是給伺服器向iframe中添加的javascript指令碼調用function addMsg(msg) {var msgElement = document.getElementById("msg");msgElement.innerHTML += msg;}</script></head><body><!-- 隱藏的iframe,src設定成CometServlet地址,用於載入伺服器推送的JS指令碼 --><iframe id="iframe" style="display: none;" src="comet"></iframe><div id="msg"></div></body></html>
後台Servlet:
public class CometServlet extends HttpServlet {@Overridepublic void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {PrintWriter out = response.getWriter();// 由於瀏覽器原因(FireFox、IE有這個問題,Chrome正常),接收到的資料不會立即輸出到頁面上。這裡首先輸入長度大於1024的字串。StringBuilder sb = new StringBuilder();for(int i = 0; i < 1024; i++) {sb.append('a');}out.println("<!-- " + sb.toString() + " -->"); // 注意加上HTML注釋out.flush();while(true) {// 這裡用Thread.sleep來類比comet,相當於每隔5秒伺服器向用戶端推送一條訊息try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}// 每隔5秒寫一段js到iframe中,去調用parent的addMsg函數,向HTML新增內容out.println("<script>parent.addMsg('helloworld<br>')</script>");out.flush(); // 這裡一定要flush,否則資料不發送}}}我在測試過程中發現FireFox、IE這些瀏覽器接收到伺服器的響應並不會立即載入,瀏覽器相當於有個緩衝,當達到一定的大小才會去載入。經測試FireFox就是1024位元組,所以我在Servlet推送訊息前先向響應中寫一個大於1024位元組的字串來避免瀏覽器不及時載入的問題。Chrome沒有這個問題。
基於iframe的流方式有個小問題就是,由於iframe一直沒有載入完成,處於串連狀態,所有瀏覽器的進度會一直顯示未完成,就是一直在轉圈。
2.2、針對FireFox的方式
在FireFox中支援Ajax的readyState為3時擷取request.responseText,也就是請求未完成就能擷取到部分響應資料。這種方式對IE等其他瀏覽器不支援。
前端頁面:
<!DOCTYPE HTML><html><head><meta http-equiv="content-type" content="text/html;charset=utf-8"><script type="text/javascript">function getMsg() {// 請求地址是CometServlet對應的URL,加上一個隨機值參數防止緩衝問題var url = "comet" + "?t="+Math.random();var request = new XMLHttpRequest();request.onreadystatechange = function() {if (request.readyState == 3) {if (request.responseText) {// 注意這裡不再是追加HTML,responseText包含之前的所有訊息document.body.innerHTML = request.responseText;}}};request.open("GET", url, true);request.send();}window.onload = getMsg;</script></head><body></body></html>後台Servlet:
public class CometServlet extends HttpServlet {@Overridepublic void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {PrintWriter out = response.getWriter();while(true) {// 這裡用Thread.sleep來類比comet,相當於每隔5秒伺服器向用戶端推送一條訊息try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}out.println("helloworld<br>");out.flush(); // 這裡一定要flush,否則資料不發送}}}
最後說明一下,本文中的後台Java代碼,只是用來學習和示範Comet,實際是不能用的。後台只是簡單的sleep沒有任何功能不說,由於Comet用的是HTTP長串連,當用戶端關閉後伺服器還在阻塞,Servlet並沒有停掉,這是很危險的。而Tomcat、Jetty都有針對Comet技術的成熟的方案可以使用,我在後續博文中也會繼續介紹。
作者:叉叉哥 轉載請註明出處:http://blog.csdn.net/xiao__gui/article/details/38331225
Java中Demo代表什
是一個啟發性的例子。
通過一個小小的例子,引導讀者的思維。可以更直觀的瞭解和掌握知識點。
很簡單的java程式製作
樓上的,這是用JavaScript來做的,而不是用java!!
建一個檔案名稱叫:clock.html,裡面代碼如下:
<html>
<head>
<title>js做的時鐘</title>
<SCRIPT type="text/javascript" >
<!--
Date.prototype.format = function(mask) {
var d = this;
var zeroize = function (value, length) {
if (!length) length = 2;
value = String(value);
for (var i = 0, zeros = ''; i < (length - value.length); i++) {
zeros += '0';
}
return zeros + value;
};
return mask.replace(/"[^"]*"|'[^']*'|\b(?:d{1,4}|m{1,4}|yy(?:yy)?|([hHMstT])\1?|[lLZ])\b/g, function($0) {
switch($0) {
case 'd': return d.getDate();
case 'dd': return zeroize(d.getDate());
case 'ddd': return ['Sun','Mon','Tue','Wed','Thr','Fri','Sat'][d.getDay()];
case 'dddd': return ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'][d.getDay()];
case 'M': return d.getMonth() + 1;
case 'MM': return zeroize(d.getMonth() + 1);
case 'MMM': return ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'][d.getMo......餘下全文>>