基于AJAX的长轮询(long-polling)方式实现简单的聊天室程序


这里只是做个测试,很简单,没有做好线程同步的问题,只是为了长轮询。

原理:

可以看:http://yiminghe.javaeye.com/blog/294781

AJAX 的出现使得 JavaScript 可以调用 XMLHttpRequest 对象发出 HTTP 请求,JavaScript 响应处理函数根据服务器返回的信息对 HTML 页面的显示进行更新。使用 AJAX 实现“服务器推”与传统的 AJAX 应用不同之处在于:

  1. 服务器端会阻塞请求直到有数据传递或超时才返回。
  2. 客户端 JavaScript 响应处理函数会在处理完服务器返回的信息后,再次发出请求,重新建立连接。
  3. 当客户端处理接收的数据、重新建立连接时,服务器端可能有新的数据到达;这些信息会被服务器端保存直到客户端重新建立连接,客户端会一次把当前服务器端所有的信息取回。

聊天页面的代码:


view plaincopy to clipboardprint?

<%@ page language="java" pageEncoding="GBK"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>chat room</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">
<mce:script type="text/javascript" src="ext-2.2/adapter/ext/ext-base.js" mce_src="ext-2.2/adapter/ext/ext-base.js"></mce:script>
<mce:script type="text/javascript" src="ext-2.2/ext-all.js" mce_src="ext-2.2/ext-all.js"></mce:script>
<mce:script type="text/javascript"
src="ext-2.2/build/locale/ext-lang-zh_CN.js"></mce:script>
<mce:script type="text/javascript" src="jslib/mm.js" mce_src="jslib/mm.js"></mce:script>
</head>
<body>
<div id="main"></div><!-- 显示聊天记录的区域 -->
username:
<input type="text" name="username" />
<br>
message:
<input type="text" id="message">
<br>
<input type="button" value="submit" onclick="putMsg()">
</body>
</html>

定义mm.js,定义发送消息,定义接收消息的JS函数


view plaincopy to clipboardprint?

Ext.onReady(function () {
getMsg();
});
function getMsg() {
Ext.Ajax.request({url:"getMsg", callback:function (options, success, response) {
if (success) {
Ext.DomHelper.append(Ext.get("main"), response.responseText, true);
}
getMsg();
}});
}
function putMsg() {
Ext.Ajax.request({url:"putMsg", params:{message:document.getElementById("message").value}});
}
/*
Ext.Updater.defaults.indicatorText='<div><img src="icon/loading.gif" mce_src="icon/loading.gif" width="20" height="20"/>refresh...</div>';
var updater = Ext.get('main').getUpdater();
updater.update({
url:"getMsg"
});
*/

 下面是获得message的servlet


view plaincopy to clipboardprint?

package hyjc.listener;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class GetMsg extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

response.setContentType("text/html");
PrintWriter out = response.getWriter();
MessageList m = MessageList.getInstance();
boolean end = false;
while (!end) {
System.out.println("before get");
String msg = m.get();
System.out.println("after get " + msg);
out.write(msg + "<br>");
if (m.isEmpty()) {
end = true;
}
}
out.close();
}

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}

}

下面是添加消息的servlet


view plaincopy to clipboardprint?

package hyjc.listener;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class PutMsg extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

response.setContentType("text/html");
System.out.println("put message");
PrintWriter out = response.getWriter();
out.flush();
String msg = request.getParameter("message");
if (null != msg) {
MessageList.getInstance().add(msg);
} else {
System.out.println("添加消息:" + msg + "成果");
}
out.close();
}

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}

}

下面是存放消息的消息队列,内部用阻塞队列使用


view plaincopy to clipboardprint?

package hyjc.listener;

import java.util.Iterator;
import java.util.concurrent.LinkedBlockingQueue;

public class MessageList {

private static MessageList list = null;

private static Object key = new Object();

private MessageList() {
this.add("hello");
this.add("world");
}

public static MessageList getInstance() {
synchronized (key) {
if (list == null) {
list = new MessageList();
}
return list;
}
}

private LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<String>();

public boolean isEmpty() {
return queue.isEmpty();
}

public int size() {
return queue.size();
}

public String get() {
try {
return queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
return null;
}
}

public void clear() {
queue.clear();
}

public void add(String msg) {
try {
queue.put(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public Iterator<String> iterator() {
return queue.iterator();
}
}

下面是演示效果,输入message,点击submit,就会添加到MessageList中,然后会在GetMsg中继续执行,实现长连接


其它相关文章

http://yiminghe.javaeye.com/blog/284953

http://yiminghe.javaeye.com/blog/294781 

http://yiminghe.javaeye.com/blog/300050