Servlet 3.0筆記之異步請求Comet推送XMLHttpRequest示范
說實話,各個瀏覽器對XMLHttpRequest內置對象請求異步連接(或者稱呼為長連接)的支持不一而足,除了Firefox,Safari,IE8等,其它的要么不支持,要么有缺陷。總之,單純依靠XHR(XMLHttpRequest的簡稱)來調用長連接,不靠譜。
XMLHttpRequest對象在標準情況下,在請求長連接情況下,readyState = 3時處于一直監聽情況,但各大主流瀏覽器內的支持相關情況不盡相同:
- IE(IE6-IE7),只有在請求的內容不再發生變化時以及 readyState = 4 的時候才會獲取到返回的responseText內容,因此不支持長連接。
- IE8:以XDomainRequest取代ActiveXObject對象,也算是一個進步,但在服務器端有邀請,必須在頭部設置 Access-Control-Allow-Origin值,若不知道客戶端調用JS所處的域名,設置成星號,通用即可。
- Opera:貌似只有在readyState=3時才會觸發一次onreadystatechange函數,不支持長連接。
- Firefox 3.6 和Safari 4:默認支持XMLHttpRequest Streaming。
- Chrome 4.1版本:只有在請求內容不再發生變化才會達到readyState=2狀態,所以支持情況不太好。
要檢測各個瀏覽器對XMLHttpRequest的支持請求,這里有一個好的去處:
Streaming Test page
Streaming Test page
以上內容,翻譯摘選自:COMET Streaming in Internet Explore
一個客戶端訂閱頁面:

頁面代碼:
<html>
<head>
<title>comet XHR測試</title>
<meta http-equiv="X-UA-Compatible" content="IE=8" />
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<meta name="author" content="yongboy@gmail.com" />
<meta name="keywords" content="servlet3, comet, ajax" />
<meta name="description" content="" />
<link type="text/css" rel="stylesheet" href="css/main.css" />
</head>
<body style="margin: 0; overflow: hidden" onload="">
<div id="showDiv" class="inputStyle"></div>
<script>
function showMsg(msg) {
document.getElementById("showDiv").innerHTML = msg;
}
function doXHR() {
var xhr = null;
// 在IE8下面,window.XMLHttpRequest = true,因此需要window.XDomainRequest放在第一位置
if (window.XDomainRequest) {
xhr = new XDomainRequest();
} else if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else if (window.ActiveXObject) {
var aVersions = [ "Msxml2.XMLHttp.5.0", "Msxml2.XMLHttp.4.0",
"Msxml2.XMLHttp.3.0", "Msxml2.XMLHttp", "Microsoft.XMLHttp" ];
for ( var i = 0; i < aVersions.length; i++) {
try {
xhr = new ActiveXObject(aVersions[i]);
break;
} catch (e) {
}
}
}
if (xhr == null) {
showMsg("當前瀏覽器不支持創建XMLHttpRequest !");
return;
}
try {
xhr.onload = function() {
showMsg(xhr.responseText);
};
xhr.onerror = function() {
showMsg("XMLHttpRequest Fatal Error.");
};
xhr.onreadystatechange = function() {
try {
if (xhr.readyState > 2) {
showMsg(xhr.responseText);
}
} catch (e) {
showMsg("XMLHttpRequest Exception: " + e);
}
};
// 經測試:
// IE8,Safayi完美支持onprogress事件(可以不需要onreadystatechange事件);
// Chrome也支持,在后臺數據推送到時,會調用其方法,但無法得到responseText值;除非(長連接關閉)
// Firefox 3.6 也支持,但得到返回值有些BUG
xhr.onprogress = function() {
showMsg(xhr.responseText);
};
xhr.open("GET", "getnew?" + Math.random(), true);
xhr.send(null);
} catch (e) {
showMsg("XMLHttpRequest Exception: " + e);
}
}
if (window.addEventListener) {
window.addEventListener("load", doXHR, false);
} else if (window.attachEvent) {
window.attachEvent("onload", doXHR);
}
</script>
</body>
</html>
當然后臺需要一個內容發布的頁面:
后臺處理的代碼和上篇隱藏IFrame服務器推送部分較為類似相似:
/**
* XHR獲取最新信息
*
* @author yongboy
* @date 2011-1-10
* @version 1.0
*/
@WebServlet(urlPatterns = "/getnew", asyncSupported = true)
public class GetNewBlogPosts extends HttpServlet {
private static final long serialVersionUID = 5656988888865656L;
private static final Log log = LogFactory.getLog(GetNewBlogPosts.class);
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.setHeader("Cache-Control", "private");
response.setHeader("Pragma", "no-cache");
response.setHeader("Connection", "Keep-Alive");
response.setHeader("Proxy-Connection", "Keep-Alive");
response.setHeader("Access-Control-Allow-Origin", "*");
response.setContentType("text/html;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
PrintWriter writer = response.getWriter();
// 若當前輸出內容較少情況下,需要產生大約2KB的垃圾數據,諸如下面產生一些空格
for (int i = 0; i < 10; i++) {
writer.print(" ");
}
writer.println("<div class='logDiv,clear'>waiting for ......</div>");
writer.flush();
final AsyncContext ac = request.startAsync();
// 設置成長久鏈接
ac.setTimeout(10 * 60 * 1000);
ac.addListener(new AsyncListener() {
public void onComplete(AsyncEvent event) throws IOException {
NewBlogXHRListener.ASYNC_XHR_QUEUE.remove(ac);
}
public void onTimeout(AsyncEvent event) throws IOException {
NewBlogXHRListener.ASYNC_XHR_QUEUE.remove(ac);
}
public void onError(AsyncEvent event) throws IOException {
NewBlogXHRListener.ASYNC_XHR_QUEUE.remove(ac);
}
public void onStartAsync(AsyncEvent event) throws IOException {
log.info("now add the AsyncContext");
}
});
NewBlogXHRListener.ASYNC_XHR_QUEUE.add(ac);
}
}
不過多了兼容IE8的代碼部分:
response.setHeader("Access-Control-Allow-Origin", "*");
在IE8以及Chrome平臺下,需要預先生成一些大小為2K的垃圾數據,諸如一些空格。
單獨線程代碼和上篇隱藏IFrame服務器推送部分相似,這里不再貼出。
單獨線程代碼和上篇隱藏IFrame服務器推送部分相似,這里不再貼出。
經測試:
- 在ubuntu 10.10系統下Chrome(版本號8.0.552.224),支持XHR Streaming,測試通過。
- 2.Firefox 3.6 下測試通過。
- Safari 5.0.3 測試通過。
- IE8測試通過。
posted on 2011-01-13 10:20 nieyong 閱讀(2362) 評論(0) 編輯 收藏 所屬分類: Servlet3