Ajax push技术核心揭秘(转)

Ajaxpush技术核心揭秘

引言

在web应用普及的今天,用户开始将更多的关键应用向Web迁移,广大Web应用开发者与推广者在享受到了成功地喜悦。与同时很多用户已经开始抱怨我们的Web应用总是那么被动与迟钝,何时才能让他们的应用更加主动实时的,让发生在服务端的事件第一时间内通知给他们。然而开发人员也不得不面对这样的事实,在Web天生的无状态与非连接制约下无法他们无法对应用的实时性更进一步的提升。

在近几年的发展特别是Aajx的出现让我们的web应用找到了新的兴奋点,然而这仍然没有解决前面提到的问题,难道我们真的无路可走了?

经过笔者的探索发现我们是可以实现基于web的实时主动通知的,即采用Ajaxpush技术,她可以让我们web应用拥有前所未有激动人心的功能和使用体验。笔者将通过本文带领逐步大家去实现这个激动人心的体验。笔者目的也非常简单,目的在于为大家提供更多的参考资料,无论对错希望本文能起到抛砖引玉的作用.如果能借此引发大家对Web实时推式通知技术的更为广泛的讨论我将感到万分荣幸!

Ajaxpush的广阔前景

通知技术笔者把它们从概念上分为两种:被动的拉式通知技术和主动的推式通知技术。这两种说法目前网上已经有大量的文章,我在这里只做简单的介绍:

1、被动的拉式通知技术又称Pull方式如下图

浏览器

服务器

internet

定时请求

根据请求响应

图:pull方式

拉方式需要客户机不定时的检查服务器已获知是否发生新的事件或数据是否有变化,这种方式并不实时,但在web上比较容易实现。

2、主动的推式通知技术又称push方式如下图

浏览器

服务器

internet

发生事件主动发送

首次建立通信连接

事件

图:push方式

推式客户机与服务只需建立好连接之后,每当服务器有特殊事件发生时才通知客户机,该方式实时性非常强,但目前在Web上实现较为复杂

这两种方式后者有非常显著的优点,而Ajaxpush就是需要在web上实现的后者的通信技术,如果Ajaxpush能被的完美实现,那么基于web的IM软件、基于Web的关键业务报警系统、基于web的实时监控系统、更智能人性的Web信息系统、甚至是基于web的远程控制系统、等等都将可以实现,同时彻底摆脱客户端部署与安装,避免服务端的高负载。而webMsn、GMAIL这些系统中的很多被我们认为是不可思议的特性也能被任何一个web程序员轻松的开发出来。这是多么美好的时刻,更加值得我们去期待。

突破观念的束缚

Web应用的优点在于易于部署,但web是无状态非连接的,从这个角度来看服务器无法对客户端进行实时的推式通知,可能会有人对我说的不屑于顾,不是已经有很多系统都实现了web上的通知吗?其实不然,目前实现web上动态通知的技术概括下来基本上有以下两种方法:

1、定期刷新法。定期刷新法又可以分为整体页面间隔刷新和异步间隔刷新两种方法。

a)整体页面间隔刷新法,该方法在早期聊天室中使用,该方法实现非常简单,只需要在Html头中加入如下代码:

<METAHTTP-EQUIV="refresh"CONTENT="10"URL="你的页面">

该方法目前已经很少使用,主要因为如果现在网页过于复杂加载时间长,如果频繁刷新会对用户造成非常差的体验,同时会传输大量重复的网络数据无疑加大了服务器及网络的负担。

b)异步间隔刷新法,由于采用异步刷新方法,即使刷新平凡都不会造成页面闪烁,同时减少了不必要的展现数据,是目前采用的较多的方法,实现上又分为Ajax刷新即XMLHttp刷新和隐藏贞刷新。我给出简单现示例如下:

Ajax刷新

varxmlhttp=newActiveXObject("Msxml2.XMLHTTP.5.0");

functionrefreshUi()

{

xmlhttp.open("POST","你接收请求的页面",true);

xmlhttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=utf-8");

xmlhttp.send(你好提交的数据);

vars=xmlhttp.responseText;//获取服务器收到的数据

UpdateUi(s);//更新你的界面上的数据

}

setInterval(refreshUi,20);

服务段可以是一个可以是jsp,asp,serverlet,aspx,等服务端处理程序,然后通过Response对象向客户端输出需要的数据即可

隐藏贞刷新

即在页面上放入隐藏的FrameSet或则IFrame,然后通过对该贞的提交操作获取数据然后刷新页面

functionrefreshUi()

{

varhiddenForm=document.frames[1].document.forms[0];//获取隐藏贞中的表单信息

hiddenForm.submit();//对表单进行提交

updateUi(hiddenForm);//对界面进行更新

}

setInterval(refreshUi,20);

但采用定期刷新法后不管是Ajax提交还是隐藏贞提交都有共同的缺点是跟新不实时,刷新速度不能过快否则将严重影响客户端或服务器的性能,同时很多客户的数据在相当长的时间内一般不会更新,这样的会造成无大量无意义的刷新,同时增大服务器的负载。但由于该方法实现简单,所以大量的web应用程序采用了该方法实现通知技术,属于非实时拉动(被动)式通知技术。

2、客户端插件法,通过为客户段编写第三方插件的形式嵌入到客户端网页中,然后同服务器建立通信连接实现即时通知技术,该技术有点可以实现服务端到客户端的主动推式通知技术,目前主要是以ActiveX或是javaApplet的方式提供插件。缺点是实现技术复杂,需要让客户安装插件,容易出现不兼容的情况,同时在目前网络病毒泛滥的情况下,很多客户端浏览器上的安全策略是禁止安装第三方的插件,这对部署和维护都带来了相当大的难度。

从上面的技术实现上看目前这两种方法目前缺点都非常明显要么非实时性,同时服务器负担巨大;要么安装插件,没有发挥web的优点。所以我们必须跳出常规方法寻找另外的方法,幸运的是经过笔者研究和尝试基本上找到实现服务器实时主动通知客户端的实现思路。

揭开神秘的面纱

事先说明,笔者在当前文章中只会提供实现的原理和参考代码段,并不提供把该功能设计成组件的相关体系结构和类设计。而关于Ajaxpush组件的设计笔者正在编写另一篇文章。

回到正题,我们先从客户端谈起,由于Web无状态非连接的特性,如果我们要在web上实现push方式,那么我们首要的是必须与服务器建立通信连接。然而我们要求是不安装任何插件,那我们首先应当想到XmlHttpRequest对象,这个非常对,那么我们如何它与服务器建立连接呢?很简单,由于该通信连接不能阻塞浏览器的主体运行所以我们不能采用同步请求方式,所以我们必须采用异步通信方式,而恰好得的XmlHttpRequest是提供的异步请求方式对于请求应答时常没有什么特别的限制,这正好符合我们与服务器建立长期连接的需求,也许微软也打算在将来的某个时间提供对浏览器push模式的支持。以下是建立长期通信连接的实例片断:

varxmlhttp=newActiveXObject("Msxml2.XMLHTTP.5.0");

functionConnectServer()

{

xmlhttp.open("POST","http://127.0.0.1:5565",true);//建立异步通信xmlhttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=utf-8");

varm=xmlhttp.send("当前浏览起的标识");

}

xmlhttp.onreadystatechange=function()

{

if(xmlhttp.readyState==4)

{

//判断返回值是否正常

if(xmlhttp.status==200)

{

//执行你的方法

vars=xmlhttp.responseText;//获取服务器发过来的信息

//执行你处理该事件的相关代码

}

}

setTimeout(ConnectServer(),100);//重新建立下一次连接

}

ConnectServer();

注意,127.0.0.1是实例的服务器,在书写程序时可以更换该地址,每次onreadystatechange被回调后应该建立另一次连接,应为每次服务器应答客户机后客户机就会断开连接,所以每次服务器应答之后客户机就应该建立新的连接以等待服务器的下一次通知。

现在客户机已经能够和服务器建立长久的连接,那么服务器该如何通知客户机呢?好的,现在我们就来解决该问题,当客户机一个xmlhttp请求发送到服务器后,服务器接收到该请求不立即应答,而将该请求挂起,直到服务器产生需要通知客户机的事件时才应答客户机。而如何挂起该请求了?有两种方案:

1、是在web服务器上实现一个Serverlet(java)或httpHandle(.net)来接收该请求,当请求的调用到来时我们可以阻塞该线程上的调用,直到服务器产生通知客户机的事件。

2、自行实现一个简单的httpserver来接收应答并处理。即用一个线程来监听指定端口,当请求到达时将用于应答的套接字(socket)存储在内存中的列表中,当服务器有事件通知时从列表中检索出对应的套接字并应答。

由于第一种方案会阻塞线程,由于web服务器应答各种请求的线程数是有限的所以第一种方案会带来性能损失和不稳定因素。所以第二种方案才是可行的。基于java的参考代码如下:

接收xmlhttp请求的代码片断

PublicstaticHashMap<String,Socket>clientSockets=newHashMap<String,Socket>();

privatebooleanserverStarted=true;

.....

.....

.....

java.net.ServerSocketss;

ss=newjava.net.ServerSocket(5565);//设置监听端口

while(serverStarted)//循环应答

{

try{

java.lang.Thread.sleep(100);

}catch(InterruptedExceptione){

break;

}

java.net.Sockets=ss.accept();

byte[]b=newbyte[s.getInputStream().available()];

s.getInputStream().read(b);

StringrequestStr=newString(b,"utf-8");

//从请求字符串中分析出客户端发来的标示符

StringclientId=parseRequestStr(requestStr);

clientSockets.put(clientId,s);

}

……..

当服务器发生事件需要通知客户段时的代码片断

java.net.Sockets=clientSockets.get(clientId);//从列表中检索出需要的客户段套接字

if(s==null)

return;

java.io.PrintWriterp=newjava.io.PrintWriter(s.getOutputStream());

p.println("HTTP/1.1200OK");

p.println("Content-Type:text/html;charset:utf-8");

p.println("Content-Length:"+msg.length());//msg为服务器要发到客户端的信息

p.println();

p.println(msg);

p.flush();

s.getOutputStream().flush();

s.getOutputStream().close();

s.close();

clientSockets.remove(clientId);//移出发送的Socket

好了倒此为止服务器主动通知客户机Ajaxpush技术的基本实现原理和参考代码段已经给出,相信大家根据笔者所给出的技术要点举一反三实现出更好的推式技术。

结束语

最后我还是要补充的说几句,笔者在文章中只是给出的关键实现,但在实际中应用该技术还需要考虑很多,我在这里举几个要考虑的比较重要点:

1、关于客户段异常,如果连接失败如何最省资源的自动再次连接,如何检测连接已经断开

2、服务器需要对列表中的套接字进行定期验证以保证列表中的连接可用

3、很多消息要连续通知客户机是对消息考虑队列的处理

好了我就说这里,如果大家有更多的问题和建议请E-mail联系我,希望大家看过本文后也能向别人问起“你的应用今天Ajaxpush了吗?”

作者信息

笔名:转瞬之间

真实姓名:蔡春茂

e-mail:caichunmao@hotmail.com

相关推荐