首頁  >  文章  >  web前端  >  Ajax的反向使用

Ajax的反向使用

php中世界最好的语言
php中世界最好的语言原創
2018-04-03 17:48:181321瀏覽

這次帶給大家Ajax的反向使用,Ajax反向使用的注意事項有哪些,下面就是實戰案例,一起來看一下。

場景1:有新郵件的時候,網頁會自動彈出提示訊息而無需使用者手動的刷新收件匣。

場景2:當使用者的手機掃描完成頁面中的二維碼以後,頁面會自動跳轉。

場景3:在類似聊天室的環境中有任何人發言,所有登入使用者都可以即時看見訊息。

與傳統的MVC模型請求必須從客戶端發起由伺服器回應相比,使用反向Ajax能夠模擬伺服器端主動向客戶端推送事件從而提高使用者體驗。本文將分成兩個部分討論反向Ajax技術,包括:Comet和WebSocket。文章旨在展示如何實現以上兩種技術手段,Struts2或SpringMVC中的應用並未涉及。此外,Servlet的配置也採用註解的方式,相關知識大家可以參考其它資料。

一、Comet(最佳的相容手段)

Comet本質上則是這樣的概念:能夠從伺服器端向客戶端發送資料。在一個標準的 HTTP Ajax 請求中,資料是發送給伺服器端的,反向 Ajax 以某些特定的方式來模擬發出一個 Ajax 請求,這樣的話,伺服器就可以盡可能快地向客戶端發送事件。由於普通HTTP請求往往會伴隨頁面的跳轉,而推播事件則需要瀏覽器停留在同一個頁面或框架下,因此Comet的實作只能夠透過Ajax來完成。

它的實作過程如下:頁面載入的時候隨即向伺服器發送一條Ajax請求,伺服器端取得請求並將它保存在一個執行緒安全的容器中(通常為隊列)。同時伺服器端仍然可以正常回應其他請求。當需要推送的事件到來的時候,伺服器遍歷容器中的請求在傳回應答後刪除。於是所有停留在頁面中的瀏覽器都會獲得該應答,並再次發送Ajax請求,重複上述過程。

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<!DOCTYPE html>
<html lang="en">
<base href="<%=basePath%>">
<head>
<title>WebSocket</title>
<script type="text/javascript" src="static/jquery-1.9.1.min.js"></script>
<script type="text/javascript">
$(function() {
connect();
$("#btn").click(function() {
var value = $("#message").val();
$.ajax({
url : "longpolling?method=onMessage&msg=" + value,
cache : false,
dataType : "text",
success : function(data) {
}
});
});
});
function connect() {
$.ajax({
url : "longpolling?method=onOpen",
cache : false,
dataType : "text",
success : function(data) {
connect();
alert(data);
}
});
}
</script>
</head>
<body>
<h1>LongPolling</h1>
<input type="text" id="message" />
<input type="button" id="btn" value="发送" />
</body>
</html>

我們注意到,由btn發送的請求其實並不需要取得回應。整個過程的關鍵是需要客戶端始終讓伺服器保持connect()的請求。而伺服器端首先需要支援這種非同步的回應方式,幸運的是目前為止絕大部分的Servlet容器都已經提供了良好的支援。以下以Tomcat為例:

package servlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(value="/longpolling", asyncSupported=true)
public class Comet extends HttpServlet {
private static final Queue<AsyncContext> CONNECTIONS = new ConcurrentLinkedQueue<AsyncContext>();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getParameter("method");
if (method.equals("onOpen")) {
onOpen(req, resp);
} else if (method.equals("onMessage")) {
onMessage(req, resp);
}
}
private void onOpen(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
AsyncContext context = req.startAsync();
context.setTimeout(0);
CONNECTIONS.offer(context);
}
private void onMessage(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String msg = req.getParameter("msg");
broadcast(msg);
}
private synchronized void broadcast(String msg) {
for (AsyncContext context : CONNECTIONS) {
HttpServletResponse response = (HttpServletResponse) context.getResponse();
try {
PrintWriter out = response.getWriter();
out.print(msg);
out.flush();
out.close();
context.complete();
CONNECTIONS.remove(context);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

ConcurrentLinkedQueue是Qu​​eue佇列的一個線程安全實現,這裡使用它來作為保存請求的容器。 AsyncContext是Tomcat支援的非同步環境,不同的伺服器使用的物件也略有不同。 Jetty支援的物件是Continuation。完成了廣播的請求需要透過context.complete()將相關請求結束,並使用CONNECTIONS.remove(context)刪除佇列。

二、WebSocket(來自HTML5的支援)

使用HTTP 長輪詢的Comet 是可靠地實現反向Ajax 的最佳方式,因為現在所有瀏覽器都提供了這方面的支援。

WebSockets 在 HTML5 中出現,是比 Comet 更新的反向 Ajax 技術。 WebSockets 支援雙向、全雙工通訊頻道,許多瀏覽器(Firefox、Google Chrome 和 Safari)也支援它。連線透過 HTTP 請求(也稱為 WebSockets 握手)和一些特殊的標頭 (header)。連線一直處於啟動狀態,您可以用 JavaScript 編寫和接收數據,就像您使用原始 TCP 套接字一樣。

透過輸入 ws:// 或 wss://(在 SSL 上)啟動 WebSocket URL。如圖:

首先:WebSockets並非在所有瀏覽器上都能獲得良好的支持,顯然IE又拖了後腿。因此當你打算使用這項技術之前必須考慮到用戶的使用環境,如果你的專案面向的是互聯網或包括手機端用戶,奉勸大家三思。

其次:WebSockets提供的請求區別於普通的HTTP請求,它是一種全雙工通訊且始終處於啟動狀態(如果你不去關閉它的話)。這意味著你不用每次獲得應答後再次向伺服器發送請求,這樣可以節省大量的資源。

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
+ path + "/";
String ws = "ws://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%>
<!DOCTYPE html>
<html lang="en">
<base href="<%=basePath%>">
<head>
<title>WebSocket</title>
<script type="text/javascript" src="static/jquery-1.9.1.min.js"></script>
<script type="text/javascript">
$(function() {
var websocket = null;
if ("WebSocket" in window){
websocket = new WebSocket("<%=ws%>websocket");
} else {
alert("not support");
}
websocket.onopen = function(evt) {
}
websocket.onmessage = function(evt) {
alert(evt.data);
}
websocket.onclose = function(evt) {
}
$("#btn").click(function() {
var text = $("#message").val();
websocket.send(text);
});
});
</script>
</head>
<body>
<h1>WebSocket</h1>
<input type="text" id="message" />
<input type="button" id="btn" value="发送"/>
</body>
</html>

JQuery对WebSocket还未提供更良好的支持,因此我们必须使用Javascript来编写部分代码(好在并不复杂)。并且打部分常见的服务器都可以支持ws请求,以Tomcat为例。在6.0版本中WebSocketServlet对象已经被标注为@java.lang.Deprecated,7.0以后的版本支持jsr365提供的实现,因此你必须使用注解来完成相关配置。

package servlet;
import java.io.IOException;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
@ServerEndpoint("/websocket")
public class WebSocket {
private static final Queue<WebSocket> CONNECTIONS = new ConcurrentLinkedQueue<WebSocket>();
private Session session;
@OnOpen
public void onOpen(Session session) {
this.session = session;
CONNECTIONS.offer(this);
}
@OnMessage
public void onMessage(String message) {
broadcast(message);
}
@OnClose
public void onClose() {
CONNECTIONS.remove(this);
}
private synchronized void broadcast(String msg) {
for (WebSocket point : CONNECTIONS) {
try {
point.session.getBasicRemote().sendText(msg);
} catch (IOException e) {
CONNECTIONS.remove(point);
try {
point.session.close();
} catch (IOException e1) {
}
}
}
}
}

三、总结(从请求到推送)

在传统通信方案中,如果系统 A 需要系统 B 中的信息,它会向系统 B 发送一个请求。系统 B 将处理请求,而系统 A 会等待响应。处理完成后,会将响应发送回系统 A。在同步 通信模式下,资源使用效率比较低,这是因为等待响应时会浪费处理时间。

在异步 模式下,系统 A 将订阅它想从系统 B 中获取的信息。然后,系统 A 可以向系统 B 发送一个通知,也可以立即返回信息,与此同时,系统 A 可以处理其他事务。这个步骤是可选的。在事件驱动应用程序中,通常不必请求其他系统发送事件,因为您不知道这些事件是什么。在系统 B 发布响应之后,系统 A 会立即收到该响应。

Web 框架过去通常依赖传统 “请求-响应” 模式,该模式会导致页面刷新。随着 Ajax、Reverse Ajax 以及 WebSocket 的出现,现在可以将事件驱动架构的概念轻松应用于 Web,获得去耦合、可伸缩性和反应性 (reactivity) 等好处。更良好的用户体验也会带来新的商业契机。

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

Ajax怎么实现智能提示关联词搜索

Ajax请求响应中打开新窗口被拦截应该如何处理

以上是Ajax的反向使用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn