http://homelink.javaeye.com/blog/293328#comments
参考文档 http://www.ibm.com/developerworks/cn/web/wa-lo-comet/
comet是HTTP长连接,就是在HTTP发送请求时,服务器不立刻发送响应信息给客户端,
而是保持着连接,等待一定情况发生后才把数据发送回去给客户端。所以用comet可以实现服务器端的数据实时地发送给客户端。
本文主要是用java和js来简单地实现comet,最后附上源码和使用例子。
在客户端用XMLRequest发送请求到服务器,在服务器端用一个servlet来接收XMLRequest的请求,当接收到请
求时,并不立刻响应客户端,而是把该servlet线程阻塞,等到一定事件发生后,再响应客户端。当客户端接收到服务端的响应后,调用自定义的回调函数来
处理服务器发送回来的数据,处理完成后,再发送一个XMLRequest请求到服务端,这样循环下去,就可以实现数据的实时更新,又不必要在客户端不断地
轮循(polling)。
利用该comet的实现(以后简称为keeper)时,只要在客户端注册事件和写一个处理返回数据的回调函数,然后在服务端实现
keeper中的EventListener接口,调用Controller.action(eventListener,eventType)就可以
了。
keeper分成两大部分,第一部分为客户端的javascript,第二部分是服务端的servlet和事件处理。
一.客户端
建立一个XMLRequest对象池,每发送一次请求,从对象池中取一个XMLRequest对象,如果没有可用的对象,则创建一
个,把它加入到对象池中。这部分的代码来自于网络。
为了使用方便,再添加一些方法,用来注册事件。这样只要调用注册函数来注册事件,并且把回调函数传给注册事件函数就行了,处理数据
的事情,交给回调函数,并由用户来实现。
keeper为了方便使用,把客户端的javascript代码集成在servlet中,当配置好keeper的servlet,
启动HTTP服务器时,keeper会根据用户的配置,在相应的目录下生成客户端的javascript代码。
二.服务端
服务端的servlet初始化时,根据配置来生成相应的客户端javascript代码。
servlet的入口由keeper.servlet.Keeper.java中的doGet进入。在Keeper的doGet
中,从请求中获取用户注册事件的名称(字符串类型),然后根据事件的名称,构造一个事件(Event类型),再把它注册到NameRegister中,注
册完成后,该servlet线程调用wait(),把自已停止。等待该servlet线程被唤醒后,从Event中调用事件的EventListener
接口的process(request,response)来处理客户端的请求。
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String eventName = request.getParameter("event");
NameRegister reg = NameRegister.getInstance();
Event event = null;
try {
event = reg.getEvent(eventName);
if(event == null) {
event = new Event(eventName,this);
reg.registeEvent(eventName, event);
}
if(event.getServlet() == null) {
event.setServlet(this);
}
} catch (RegistException e1) {
e1.printStackTrace();
}
synchronized(this) {
while(!event.isProcess()) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
EventListener listener = event.getListener();
if(listener != null) {
listener.process(request,response);
}
}
在服务端处理事件时,调用了keeper.control.Controller中的静态方法
action(EventListener listener,String eventName)来处理。如下所示。
public static boolean action(EventListener listener,String eventName){
NameRegister reg = NameRegister.getInstance();
HttpServlet servlet = null;
Event e = null;
try {
e = reg.getEvent(eventName,true);
if(e == null) {
return false;
}
e.setListener(listener);
servlet = e.getServlet();
e.setProcess(true);
synchronized(servlet) {
servlet.notifyAll();
}
} catch (RegistException ex) {
ex.printStackTrace();
}
if(servlet != null && e != null) {
e = null;
return true;
} else {
return false;
}
}
下面开始用keeper来写一个简单的网页聊天程序和基于服务端的时间。
1.客户端设置
注册两个事件,一个用于是时间事件,一个是消息事件。同时还要写两个回调函数,用于处理服务
端返回的时间和聊天消息。如下所于:
Keeper.addListener('timer',showTime);//注册时间事件
function showTime(obj){ //时间处理回调函数
var sp = document.getElementById("dateTime");
if(sp){
sp.innerHTML = obj.responseText;
}
}
function startOrStop(obj){
var btn = document.getElementById("controlBtn")
btn.value=obj.responseText;
}
Keeper.addListener('msg',showMsg,"GBK");//注册消息事
件,最后一个参数是
//字符串编码
function showMsg(obj){//处理消息的回调函数
var msg = document.getElementById("msg");
if(msg){
msg.value = obj.responseText+""n"+msg.value;
}
}
function sendMsg() {
var msg = document.getElementById("sendMsg");
if(msg){
var d = "msg="+msg.value;
sendReq('POST','./demo',d,startOrStop);
msg.value = "";
}
}
2.配置服务端
服务端的配置在
web.xml文件中,如下所示
keeper
keeper.servlet.Keeper
ScriptName
/keeperScript.js
1
keeper
/keeper
用在页面包含JavaScript时,这里的src一定要和上面配
置的一至。上面的设置除了为可选的设置外,其他的都是必要的,而且不能改
变。
3.编写事件处理代码,消息的处理代码如下:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("Post..");
String msg = request.getParameter("msg");
Controller.action(new SendMsg(msg),"msg");
}
class SendMsg implements EventListener{
private String msg;
public SendMsg(String msg) {
this.msg = msg;
}
@Override
public void process(HttpServletRequest request, HttpServletResponse response) {
response.setCharacterEncoding("UTF-8");
PrintWriter out = null;
try {
out = response.getWriter();
if(msg!=null){
out.write(msg);
}
} catch (IOException e) {
e.printStackTrace();
}
finally{
if(out != null) {
out.close();
}
}
}
}
到这时,一个基本的keeper应用就完成了。其它部分请参考附件中的例子源码。
描述: comet的实现和应用例子
下载次数: 767