Web页面实时刷新技术探讨

一、总述

随着网络技术的飞速发展,使用B/S结构来实现项目应用已经越来越多,而实时监控一直都是多数行业软件所必备的功能,由此使用Web页面来实现实时监控成了一种必然的需求。

 

二、实时刷新技术

1、传统的页面刷新方式

传统的页面刷新方式很多,常见的有页面间隔一定的时间自动刷新、ActiveX控件、Applet等。

采用页面间隔一定的时间自动刷新的方式,是在网页的头部加入一下代码:

<meta http-equiv="refresh" content="20;url=newPage">

这里是经过20秒跳转到一个新页面,可以将“newPage”设置为本页面即为刷新本页面,刷新间隔时间可以修改“20”为任意时间。通过这种方式如果并发和访问量较大,服务器就有可能承受不了这种压力,从而造成服务器死机。

使用ActiveX控件的方式需要每个客户端下载安装ActiveX控件,并且客户端浏览器只能使用Windows的IE浏览器。

同样使用Applet需要客户端安装Java运行时。

这些传统的页面刷新方式都或多或少的存在着一些确定,在Web项目应用中的使用也越来越少。

 

2、Ajax轮询

Ajax轮询方式是使用客户端脚本,通过XMLHttpRequest来定时发送请求,从而查询页面数据的更新情况。通过这种方式,程序实现方便简捷,但客户端频繁的发送请求会给服务器带来很大的压力和客户端处理器负载,如果服务器端没有更新时,这种轮询访问服务器便是无意义的,并且耗费了网络资源与CPU处理资源。

实例说明:服务器端通过手动控制按钮产生一张图片,客户端显示最新图片及图片的信息内容。

服务器端通过一个按钮btnGet产生图片,按钮事件代码如下所示。

代码清单1:

protected void btnGet_Click(object sender, EventArgs e)

{

    //通过改写一张父图片上的文字来产生新图片

    System.Drawing.Image image = System.Drawing.Image.FromFile(HttpContext.Current.Server.MapPath("parent.jpg"));

    string currTime = System.DateTime.Now.ToString("yyMMddHHmmssffffff");

 

    Graphics g = Graphics.FromImage(image);

    g.DrawImage(image, 0, 0, image.Width, image.Height);

    g.DrawString(currTime, new Font("Arial", 28), new SolidBrush(Color.Red), 10, 10);

    g.Dispose();

 

    string savePath = "Pic/" + currTime + ".jpg";

    image.Save(HttpContext.Current.Server.MapPath(savePath));

 

    //将最新图片文件名写入到XML文件中

    XmlDocument xmlDoc = new XmlDocument();

    xmlDoc.Load(HttpContext.Current.Server.MapPath("newPic.xml"));

    XmlNodeList nodeList = xmlDoc.SelectSingleNode("Items").ChildNodes;

 

    XmlElement element = (XmlElement)nodeList[0];

    element.SetAttribute("code", currTime);

    xmlDoc.Save(HttpContext.Current.Server.MapPath("newPic.xml"));

}

显示图片页面通过两个页面分别显示图片信息与图片内容,显示图片页面内容如下所示。

代码清单2:

<html xmlns="http://www.w3.org/1999/xhtml" >

<head runat="server">

    <title></title>

    <meta http-equiv="Content-Type" content="text/html; Charset=gb2312" />

    <script type="text/javascript">

        var xmlHttp;

        function CreateXMLHttp() {

            if(window.XMLHttpRequest) {

                xmlHttp = new XMLHttpRequest();

            }

            else if (window.ActiveXObject) {

                try { xmlHttp = new ActiveXObject("Msxml2.XMLHTTP"); }

                catch(e) {

                    try { xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); }

                    catch(e) { }

                }

            }

            xmlHttp = new ActiveXObject("Msxml2.XMLHTTP.5.0");

        }

 

        function startXMLHttp() {

            CreateXMLHttp();

            xmlHttp.onreadystatechange =retDeal;

            xmlHttp.open("post","imgInfo.aspx",true);

            xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded charset=gb2312");

            xmlHttp.send();

        }       

     

         function retDeal() {

           if(xmlHttp.readystate==4) {

             if(xmlHttp.status==200) {

               hid1.value = xmlHttp.responseText;

               if(hid1.value != hid2.value) {

                 hid2.value = hid1.value;

                 ifrImg.location.reload();

                 document.getElementById("Content").innerHTML = hid1.value;

               }

             }

             setTimeout(startXMLHttp,2000);

          }

        }

    </script>

</head>

<body onload='Javascript:startXMLHttp()'>

    <div></div>

    <span id="Content"></span>

    <input type="hidden" id="hid1" /><input type="hidden" id="hid2" />

    <iframe id="ifrImg" src="img.aspx" width="800" height="500"></iframe>

</body>

</html>

 

3、DWR服务器Push

DWR的反转AJAX功能允许我们从服务器端来控制客服端,而不需要客户端的请求,服务器可以自动把消息发给指定的客户端。DWR的Push技术是让服务器每次发送广播时,把这个广播推送给客户端,而不用客户端去刷新,DWR的推送是基于长连接的,性能优越。

以服务器端通过手动控制按钮产生一张图片,客户端显示最新图片及图片的信息内容作为实例加以说明。

服务器端通过一个按钮产生图片,页面代码如下所示。

代码清单3:

<%@ page language="java" pageEncoding="UTF-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

<title></title>

<script type='text/javascript'src='<%=request.getContextPath()%>/dwr/interface/getPic.js'></script>

<script type='text/javascript' src='<%=request.getContextPath()%>/dwr/engine.js'></script>

<script type='text/javascript' src='<%=request.getContextPath()%>/dwr/util.js'></script>

<script type="text/javascript">

    Date.prototype.format = function(format) {

    var o = {

        "M+" : this.getMonth()+1, //month

        "d+" : this.getDate(),    //day

        "h+" : this.getHours(),   //hour

        "m+" : this.getMinutes(), //minute

        "s+" : this.getSeconds(), //second

        "q+" : Math.floor((this.getMonth()+3)/3), //quarter

        "S" : this.getMilliseconds() //millisecond

        }

    if(/(y+)/.test(format)) format=format.replace(RegExp.$1,

        (this.getFullYear()+"").substr(4 - RegExp.$1.length));

    for(var k in o)if(new RegExp("("+ k +")").test(format))

        format = format.replace(RegExp.$1,

            RegExp.$1.length==1 ? o[k] :

                ("00"+ o[k]).substr((""+ o[k]).length));

    return format;

    }

 

    function getNewPic() {

        var currTime = new Date().format("yyMMddhhmmssS");

        var currPath = "D:/Program/Java/JavaSpace/ajaxTest/WebContent/";

       getPic.createStringMark(currPath+"parent.jpg",currTime,currPath+"Pic/"+currTime+".jpg");

        getPic.getNewPicId(currTime);

        }

</script>

</head>

<body>

    <input type="button" value="产生新图片" onclick="getNewPic();" />

</body>

</html>

显示图片页面通过两个页面分别显示图片信息与图片内容,显示图片页面内容如下所示。

代码清单4:

<%@ page language="java" pageEncoding="UTF-8"%>

<jsp:useBean id="aGetNewPic" scope="page" class="com.getNewPic" />

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

<title></title>

<script type='text/javascript'src='<%=request.getContextPath()%>/dwr/interface/getPic.js'></script>

<script type='text/javascript' src='<%=request.getContextPath()%>/dwr/engine.js'></script>

<script type='text/javascript' src='<%=request.getContextPath()%>/dwr/util.js'></script>

<script type="text/javascript">

    function init() {

        dwr.engine.setActiveReverseAjax(true);    //激活反转

        }

    window.onload = init;//页面初始化方法

    function refreshImg() {

        ifrImg.location.reload();

        }

</script>

</head>

<body>

    <div style=" float:left">最新图片:</div>

    <span id="divNewPicId"><%= aGetNewPic.currPicId %></span>

    <iframe id="ifrImg" src="img.jsp?id=<%= aGetNewPic.currPicId %>" width="800"height="500"></iframe>

</body>

</html>

另外,getNewPic类用于产生新图片、Push处理。

代码清单5:

public class getNewPic {

      

       public static String currPicId = "100413101427820";

      

       public String getNewPicId(String picId, HttpServletRequest request){

              if(currPicId == picId)return currPicId;

              if(picId != null) currPicId = picId;

             

              //获得DWR上下文

              ServletContext sc = request.getSession().getServletContext();

              ServerContext sctx = ServerContextFactory.get(sc);

              //获得当前浏览 client.jsp 页面的所有脚本session

              Collection sessions = sctx.getScriptSessionsByPage("/ajaxTest/client.jsp");

              Util util = new Util(sessions);

              //处理这些页面中的一些元素

              util.setValue("divNewPicId", currPicId);

              util.addFunctionCall("refreshImg",null);

             

              return currPicId;

       }

      

       public boolean createStringMark(String filePath,String markContent,String savePath)

       {

           ImageIcon imgIcon=new ImageIcon(filePath);

           Image theImg =imgIcon.getImage();

           int width=theImg.getWidth(null);

           int height= theImg.getHeight(null);

           //System.out.println(theImg);

           BufferedImage bimage = new BufferedImage(width,height, BufferedImage.TYPE_INT_RGB);

           Graphics2D g=bimage.createGraphics();

           g.setColor(Color.red);

           g.setBackground(Color.white);

           g.drawImage(theImg, 0, 0, null );

           g.setFont(new Font("Arial",Font.PLAIN,28)); //字体、字型、字号

           g.drawString(markContent,10,10); //画文字

           g.dispose();

           try

           {

                  FileOutputStream out=new FileOutputStream(savePath); //输出文件名

                  JPEGImageEncoder encoder =JPEGCodec.createJPEGEncoder(out);

                  JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(bimage);

                  param.setQuality(1, true);

                  encoder.encode(bimage, param);

                  out.close();

              }

           catch(Exception e)

           { return false; }

           return true;

       }

 

}

 

4、与服务端建立长连接

与服务器建立长连接,也就是在显示数据页面中嵌入一个隐藏页面,该隐藏页面主要完成取服务器端所要显示的数据,并且将该页面显示数据的方法写成一个死循环,以此来保持与服务器端的长连接。

同样以服务器端通过手动控制按钮产生一张图片,客户端显示最新图片及图片的信息内容作为实例加以说明。

服务器端通过一个按钮btnGet产生图片,按钮事件代码同代码清单1。

显示图片页面通过两个页面分别显示图片信息与图片内容,显示图片页面内容如下所示。

代码清单6:

<html xmlns="http://www.w3.org/1999/xhtml">

<head runat="server">

    <title></title>

<script type="text/javascript">

    function writePicInfo(str) {

        if (window.document.getElementById("divNewPicId").innerText != str) {

            window.document.getElementById("divNewPicId").innerText = str;

            ifrImg.location.reload();

        }

    }

    function onload(){

        var ifrpush = new ActiveXObject("htmlfile"); // 创建对象

        ifrpush.open();

        var ifrDiv = ifrpush.createElement("div"); //添加一个DIV

        ifrpush.appendChild(ifrDiv); //添加到htmlfile

        ifrpush.parentWindow.writePicInfo = writePicInfo; //注册javascript方法

        ifrDiv.innerHTML = "<iframe src='getNew.aspx'></iframe>"//在div里添加iframe

        ifrpush.close();

    }

     onload();

    </script>

</head>

<body>

        <div style=" float:left">最新图片:</div>

        <div id="divNewPicId"></div>

        <iframe id="ifrImg" src="img.aspx" width="800" height="500"></iframe>

</body>

</html>

其中,隐藏页面getNew.aspx代码如下所示。

代码清单7:

protected override void Render(HtmlTextWriter output)

{

    string str;

    while (true)  //死循环保持长链接

    {

        //读取最新图片信息

        XmlDocument xmlDoc = new XmlDocument();

        xmlDoc.Load(HttpContext.Current.Server.MapPath("newPic.xml"));

        XmlNodeList nodeList = xmlDoc.SelectSingleNode("Items").ChildNodes;

        XmlElement element = (XmlElement)nodeList[0];

        string newPicId = element.GetAttribute("code");

 

        str = "<script >window.parent.writePicInfo('" + newPicId + "')</script>";

        this.Context.Response.Write(str);

        this.Context.Response.Flush();

        System.Threading.Thread.Sleep(2000);

    }

}

代码中的“htmlfile”是一个类似JavaScript中Window对象的一个ActiveX Object,它内部也是DOM结构,将作为隐藏帧的IFrame写入这个对象中,这样可以解决进度条一直为读取状态的问题。

 

5、RTMP协议传输

随着网络技术的迅猛发展,视频、音频等多媒体通信需求越来越多,Adobe公司开放了RTMP(the Real-time Messaging Protocol)协议规范,RTMP协议作为客户端和服务器端的传输协议,这是一个专门为高效传输视频、音频和数据而设计的TCP/IP 协议。其优秀产品Flex是用于构建和维护在所有主要浏览器、桌面和操作系统一致地部署的极具表现力的 Web 应用程序的高效率的开放源码框架。

从目前的应用来说,RTMP主要用于音、视频的传输,流视频服务器就是FMS(Flash Media Server),其原称为FCS(Flash Communication Server),技术范畴能应用到诸如Flash聊天室、视频会议等领域。

以一个实现聊天功能的Flex程序为例,显示聊天内容代码如下所示。

代码清单8:

<?xml version="1.0" encoding="utf-8"?>

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" fontSize="12" creationComplete="init()">

 

<mx:Script>

    <![CDATA[

   

    import vo.Message;

    import mx.collections.ArrayCollection;

    import mx.controls.Alert;

   

    private var myNetConnection:NetConnection;  //flex fms 链接用的对象

    private var serverApp:String ="rtmp://127.0.0.1/Test";

    private var talk_so:SharedObject;  //fms 下的SharedObject 对象

      

    private function init():void

    {

       btn_send.addEventListener(MouseEvent.CLICK,btnSenClickHandler);

       myNetConnection = new NetConnection();

       myNetConnection.addEventListener(NetStatusEvent.NET_STATUS,netStatusHandler);   

       myNetConnection.connect(serverApp);

    }

   

    private function netStatusHandler(evt:NetStatusEvent):void

    {

       trace(evt.info.code);  //调试代码用     

       if ( evt.info.code =="NetConnection.Connect.Success" )

       {

           talk_so = SharedObject.getRemote("talk",myNetConnection.uri,true);

           talk_so.addEventListener(SyncEvent.SYNC,talkSoSyncHandler);

           talk_so.connect(myNetConnection);

       }

       else

       {

           Alert.show("链接失败"+evt.info.code);

       }

    }

   

    private function talkSoSyncHandler(evt:SyncEvent):void

    {

       txt_content.text="";

       if ( talk_so.data.msgList!=null )

       {

           var tmp:ArrayCollection = new ArrayCollection();

           convertArrayCollection(tmp,talk_so.data.msgList as ArrayCollection);        

           for(var i:int=0;i<tmp.length ;i++)

           {

              var message:Object = tmp.getItemAt(i);          

              varfullMsg:String=message.nickname+""+message.time.toTimeString()+":"+message.msg;          

              txt_content.text=txt_content.text+fullMsg+"/n";

           }

       }

    }

      

    private function btnSenClickHandler(evt:MouseEvent):void

    {

       var arr:ArrayCollection = new ArrayCollection();    

       if ( talk_so.data.msgList==null )

       {

           arr = new ArrayCollection();   

       }

       else

       {

           convertArrayCollection(arr,talk_so.data.msgList as ArrayCollection);

       }     

       var obj:Message = new Message();

       obj.nickname=txt_nickname.text;

       obj.msg=txt_message.text;

       obj.time = new Date();     

       arr.addItem(obj);   

       talk_so.setProperty("msgList",arr);

       /*将你更新好的聊天记录列表写入到公共的SharedObject对象中去即可。

           调用setProperty() 以更改数据对象的属性。 服务器将更新这些属性,并调度 sync 事件,并将这些属性发回到连接的客户端。*/

       txt_message.text="";

    }

   

    //交换数组中元素

    private functionconvertArrayCollection(arrNew:ArrayCollection,arrOld:ArrayCollection):void

    {

       arrNew.removeAll();     

       for(var i:int=0;i<arrOld.length ;i++)

       {

           arrNew.addItemAt(arrOld.getItemAt(i),i);

       }

    }     

    ]]>

</mx:Script>

 

    <mx:TextArea x="33" y="10" height="159" width="366" id="txt_content"/>

    <mx:TextInput x="33" y="177" width="62" id="txt_nickname"/>

    <mx:Label x="103" y="179" text=""/>

    <mx:TextInput x="146" y="177" width="185" id="txt_message"/>

    <mx:Button x="334" y="177" label="send" id="btn_send"/>

   

</mx:Application>

 

三、结语

从目前实际应用来说,以上四种实现Web页面实时刷新都是可行方案,各有优缺点与适用的具体环境。

Ajax轮询方式比较适用于需要传输的数据量较小的情况,可通过客户端首先轮询服务器端的更新标识,若有更新再下载更新数据,这样能减小一部分服务器的压力。

DWR反转AJAX功能,真正实现了从服务器端将更新“推”到客户端。

与服务器端建立长连接的方式,也是通过客户端的请求获取更新数据的。

通过RTMP协议传输,主要适用于音、视频的数据传输,比较适用于视频聊天室,目前视频服务器FMS的费用较高(4500元100个客户端),与服务器的连接数受到限制。

总体来看,要实现Web页面的实时刷新,肯定是会给服务器带来一定的压力的,对于具体项目的不同需求,可选择合适的方式来实现Web页面的实时刷新。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值