上节实现了Ajax功能,本节实现服务器的推送。
首先了解一下什么是服务器推送技术和DWR的推送方式。
1.服务器推送技术和DWR的推送方式
传统模式的 Web 系统以客户端发出请求、服务器端响应的方式工作,服务端不能主动发送请求(消息)给客户端。
这种方式并不能满足很多现实应用的需求,譬如:
监控系统:后台硬件热插拔、LED、温度、电压发生变化;
而基于客户端套接口的“服务器推”技术大体可以分为:
1.传统轮询:在 Web 早期,这一点常使用 Meta 刷新实现。这将自动指示浏览器在指定秒数之后重新装载页面,从而支持简陋的轮询( polling )。例如在 HTML 文件中加入 <Meta HTTP-RQUIV="Refresh" CONTENT=12> ,实际上就是 HTTP 头标告知浏览器每 12 秒更新一次文档。
优点:不需要服务器端配置
缺点:用户体验度差
对服务器的压力很大,带宽流失严重
2.ajax轮询:Ajax 隔一段时间(通常使用 JavaScript 的 setTimeout 函数)就去服务器查询是否有改变,从而进行增量式的更新。但是间隔多长时间去查询成了问题,因为性能和即时性造成了严重的反比关系。间隔太短,连续不断的请求会冲垮服务器,间隔太长,务器上的新数据就需要越多的时间才能到达客户机。
优点:不需要太多的服务器端配置
降低了带宽的负荷(因为返回的不是完整的页面)
缺点:对服务器的压力并没有减少
实时性差,有一定的延迟
3.comet:Comet 方式通俗的说就是一种长连接机制 (long lived http) 。同样是由 Browser 端主动发起请求,但是 Server 端以一种似乎非常慢的响应方式给出回答。这样在这个期间内,服务器端可以使用同一个 connection 把要更新的数据主动发送给 Browser 。因此请求可能等待较长的时间,期间没有任何数据返回,但是一旦有了新的数据,它将立即被发送到客户机。Comet 又有很多种实现方式,但是总的来说对 Server 端的负载都会有增加 . 虽然对于单位操作来说,每次只需要建议一次 connection,但是由于 connection 是保持较长时间的,对于 server 端的资源的占用要有所增加。
缺点:长期占用连接,丧失了无状态高并发的特点。
应用:股票系统。实时通讯。
4.flash xml socket:这种方案实现的基础是:一、 Flash 提供了 XMLSocket 类。二、 JavaScript 和 Flash 的紧密结合:在 JavaScript 可以直接调用 Flash 程序提供的接口。
因为是使用套接口,需要设置一个通信端口,防火墙、代理服务器也可能对非 HTTP 通道端口进行限制;
应用:网络聊天室,网络互动游戏。
5.java appet:在客户端使用 Java Applet ,通过 java.net.Socket 或 java.net.DatagramSocket 或 java.net.MulticastSocket 建立与服务器端的套接口连接,从而实现 “ 服务器推送 ” 。
DWR采用的是长连接机制。
1、长连接技术通过客户端发出请求获取服务器端数据的方式通常称为"拉"技术,很形象说明客户端在拉取服务器端数据,而有时候需要服务器端主动向客户端"推"数据,比如监测聊天上线人数主动向上线发送消息,后台数据库发生变化是主动更新所有客户端展示。
2、ReverseAjax实现服务器推技术DWR2.x的推技术也叫DWRReverseAjax(逆向Ajax)主要是在BS架构中,从服务器端向多个浏览器主动推数据的一种技术。DWR的逆向Ajax主要包括两种模式:主动模式和被动模式。其中主动模式包括polling和comet两种,被动模式只有piggyback这一种:
(1)piggyback方式,是默认的方式。如果后台有什么内容需要推送到前台,是要等到那个页面进行下一次ajax请求的时候,将需要推送的内容附加在该次请求之后,传回到页面。只有等到下次请求页面主动发起了,中间的变化内容才传递回页面。
(2)comet方式当服务端建立和浏览器的连接,将页面内容发送到浏览器之后,对应的连接并不关闭,只是暂时挂起。如果后面有什么新的内容需要推送到客户端的时候直接通过前面挂起的连接再次传送数据。服务器所能提供的连接数目是一定的,在大量的挂起的连接没有关闭的情况下,可能造成新的连接请求不能接入,从而影响到服务质量。
(3)polling方式由浏览器定时向服务端发送ajax请求,询问后台是否有什么内容需要推送,有的话就会由服务端返回推送内容。这种方式和我们直接在页面通过定时器发送ajax请求,然后查询后台是否有变化内容的实现是类似的。
2.ScriptSession的生命周期
本节实现的基本功能:
package com.DWR.dwr; import java.util.Collection; import org.directwebremoting.Browser; import org.directwebremoting.ScriptBuffer; import org.directwebremoting.ScriptSession; public class MessagePush { public void sendMessage(final String content){ Runnable run = new Runnable(){ ScriptBuffer script = new ScriptBuffer(); public void run(){ //设置要调用的 js及参数 script.appendCall("showMessage",content); //得到所有ScriptSession Collection<ScriptSession> sessions = Browser.getTargetSessions(); for(ScriptSession session :sessions){ session.addScript(script); } } }; //执行推送 Browser.withAllSessions(run); } }
2.新建receive.jsp用来接收来自服务器的消息:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'receive.jsp' starting page</title> <Meta http-equiv="pragma" content="no-cache"> <Meta http-equiv="cache-control" content="no-cache"> <Meta http-equiv="expires" content="0"> <Meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <Meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> <script src="jquery-1.7.js"></script> <script type= "text/javascript" src ="dwr/util.js"></script> <script type="text/javascript" src= "dwr/engine.js"></script > <script type="text/javascript" src= "dwr/interface/messagePush.js" ></script> <script> //这个方法用来启动该页面的ReverseAjax功能 dwr.engine.setActiveReverseAjax(true); //设置在页面关闭时,通知服务端销毁会话 dwr.engine.setNotifyServerOnPageUnload(true); //这个函数是提供给后台推送的时候 调用的 function showMessage(content){ $('#getMessage').append(content); }; </script> </head> <body> <div id="getMessage" style="padding:100px;border:1px solid #F00;"> <ul></ul> </div> </body> </html>
3.web.xml加入activeReserveAjax功能
<init-param> <param-name>activeReverseAjaxEnabled</param-name> <param-value>true</param-value> </init-param>