2、客户端插件法,通过为客户段编写第三方插件的形式嵌入到客户端网页中,然后同服务器建立通信连接实现即时通知技术,该技术有点可以实现服务端到客户端的主动推式通知技术,目前主要是以ActiveX或是java
Applet的方式提供插件。缺点是实现技术复杂,需要让客户安装插件,容易出现不兼容的情况,同时在目前网络病毒泛滥的情况下,很多客户端浏览器上的安全策略是禁止安装第三方的插件,这对部署和维护都带来了相当大的难度。
从上面的技术实现上看目前这两种方法目前缺点都非常明显要么非实时性,同时服务器负担巨大;要么安装插件,没有发挥web的优点。所以我们必须跳出常规方法寻找另外的方法,幸运的是经过笔者研究和尝试基本上找到实现服务器实时主动通知客户端的实现思路。
揭开神秘的面纱
事先说明,笔者在当前文章中只会提供实现的原理和参考代码段,并不提供把该功能设计成组件的相关体系结构和类设计。而关于Ajax
push组件的设计笔者正在编写另一篇文章。
回到正题,我们先从客户端谈起,由于Web无状态非连接的特性,如果我们要在web上实现push方式,那么我们首要的是必须与服务器建立通信连接。然而我们要求是不安装任何插件,那我们首先应当想到XmlHttpRequest对象,这个非常对,那么我们如何它与服务器建立连接呢?很简单,由于该通信连接不能阻塞浏览器的主体运行所以我们不能采用同步请求方式,所以我们必须采用异步通信方式,而恰好得的XmlHttpRequest是提供的异步请求方式对于请求应答时常没有什么特别的限制,这正好符合我们与服务器建立长期连接的需求,也许微软也打算在将来的某个时间提供对浏览器push模式的支持。以下是建立长期通信连接的实例片断:
var
xmlhttp = new ActiveXObject("Msxml2.XMLHTTP.5.0");
function ConnectServer()
{
xmlhttp.open("POST","http://127.0.0.1:5565",true);//建立异步通信 xmlhttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=utf-8");
var m=xmlhttp.send("当前浏览起的标识");
}
xmlhttp.onreadystatechange = function()
{
if(xmlhttp.readyState == 4)
{
//判断返回值是否正常
if(xmlhttp.status == 200)
{
//执行你的方法
var s=xmlhttp.responseText;//获取服务器发过来的信息
//执行你处理该事件的相关代码
}
}
setTimeout(ConnectServer(),100);//重新建立下一次连接
}
ConnectServer();
注意,127.0.0.1是实例的服务器,在书写程序时可以更换该地址,每次onreadystatechange被回调后应该建立另一次连接,应为每次服务器应答客户机后客户机就会断开连接,所以每次服务器应答之后客户机就应该建立新的连接以等待服务器的下一次通知。
现在客户机已经能够和服务器建立长久的连接,那么服务器该如何通知客户机呢?好的,现在我们就来解决该问题,当客户机一个xmlhttp请求发送到服务器后,服务器接收到该请求不立即应答,而将该请求挂起,直到服务器产生需要通知客户机的事件时才应答客户机。而如何挂起该请求了?有两种方案:
1、 是在web服务器上实现一个Serverlet(java)或httpHandle(.net)来接收该请求,当请求的调用到来时我们可以阻塞该线程上的调用,直到服务器产生通知客户机的事件。
2、 自行实现一个简单的httpserver来接收应答并处理。即用一个线程来监听指定端口,当请求到达时将用于应答的套接字(socket)存储在内存中的列表中,当服务器有事件通知时从列表中检索出对应的套接字并应答。
由于第一种方案会阻塞线程,由于web服务器应答各种请求的线程数是有限的所以第一种方案会带来性能损失和不稳定因素。所以第二种方案才是可行的。基于java的参考代码如下:
接收xmlhttp请求的代码片断
Public static HashMap
Socket> clientSockets=new
HashMap
Socket>();
private boolean serverStarted=true;
.....
.....
.....
java.net.ServerSocket ss;
ss
=
new java.net.ServerSocket(5565);//设置监听端口
while(serverStarted) //循环应答
{
try {
java.lang.Thread.sleep(100);
} catch (InterruptedException e) {
break;
}
java.net.Socket s
=ss.accept();
byte[] b =new byte[s.getInputStream().available()] ;
s.getInputStream().read(b);
String requestStr=new String(b,"utf-8");
//从请求字符串中分析出客户端发来的标示符
String
clientId=parseRequestStr(requestStr);
clientSockets.put(clientId,s);
}
……..
当服务器发生事件需要通知客户段时的代码片断
java.net.Socket s=clientSockets.get(clientId);//从列表中检索出需要的客户段套接字
if(s==null)
return;
java.io.PrintWriter
p=new java.io.PrintWriter(s.getOutputStream());
p.println("HTTP/1.1 200OK");
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
好了倒此为止服务器主动通知客户机Ajax
push技术的基本实现原理和参考代码段已经给出,相信大家根据笔者所给出的技术要点举一反三实现出更好的推式技术。
结束语
最后我还是要补充的说几句,笔者在文章中只是给出的关键实现,但在实际中应用该技术还需要考虑很多,我在这里举几个要考虑的比较重要点:
1、 关于客户段异常,如果连接失败如何最省资源的自动再次连接,如何检测连接已经断开
2、 服务器需要对列表中的套接字进行定期验证以保证列表中的连接可用
3、 很多消息要连续通知客户机是对消息考虑队列的处理
好了我就说这里,如果大家有更多的问题和建议请E-mail联系我,希望大家看过本文后也能向别人问起“你的应用今天Ajax
push了吗?”