根据网上一些reverse ajax例子,自己随便写了个群内聊天的例子,只实现了群聊天,其它一些杂七杂八的都没实现,写这么个功能只是学习下reverse ajax而已,了解服务器推技术。
开发工具:eclipse 3.4 纯净版
环境:tomcat 6
技术:DWR
工程类说明:
ChatManager.java 聊天实现类
Message.java 消息封装类
OnlineCounter.java 在线人数计算方法
OnlineCounterListener.java 统计在线人数
User.java 用户bean
页面:
index.jsp --输入http:127.0.0.1:8080/ichat自动访问此页面
ShowModel_Frames.jsp --登录之后群聊天的主界面
example.jsp --FCKEDIT编辑器页面
excute_sent.jsp -- 消息发送页面
online_list.jsp --在线列表页面
show_msg.jsp --显示消息页面
代码:
ChatManager.java 聊天实现类:
package com.ccic.chat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.directwebremoting.ScriptSession;
import org.directwebremoting.ServerContextFactory;
import org.directwebremoting.WebContext;
import org.directwebremoting.WebContextFactory;
import org.directwebremoting.proxy.dwr.Util;
/**
* 处理聊天相关
*
*
*
*/
public class ChatManager {
/** 保存当前在线用户列表 */
public static List<User> users = new ArrayList<User>();
/**
* 更新在线用户列表
* @param request
*/
public void updateUsersList(HttpServletRequest request) {
User user = null;
String flag="0";//标识用户,0:不存在 1:已存在
String name= request.getSession().getAttribute("uname").toString();
String pwd= request.getSession().getAttribute("pwd").toString();
System.out.println("用户ID="+name+" 用户密码="+pwd);
user = new User(pwd, name);
//保存用户到列表
//如果列表中无任何用户则添加,否则循环查找列表中是否已存在该用户,
//如果不存在,则添加。如果存在,则不添加
if(users.size() == 0){
users.add(user);
}else{
for(int i=0;i<users.size();i++){
User us =(User)users.get(i);
if(us.getUsername().equals(name)){
flag="1";
break;
}
}
if(flag.equals("0")){
users.add(user);
}
}
/*//统计在线人数
long count=OnlineCounter.getOnline();
StringBuffer sb=new StringBuffer();
sb.append("<script language='JavaScript' defer='defer'>");
sb.append("d = new dTree(\'d\');");
sb.append("d.add(0,-1,'在线用户列表(当前"+count+"人)');");
for(int i=0;i<users.size();i++){
User us =(User)users.get(i);
sb.append("d.add("+(i+1)+",0,'"+us.getUsername()+"','','','');");
}
sb.append("document.write(d);");
sb.append("d.config.useCookies=false;");
sb.append("d.config.closeSameLevel=true;");
sb.append("</script");//注意这里并不是好了“>”括号,而是在页面另有处理
System.out.println("dd="+sb.toString());*/
//将用户id和页面脚本session绑定
//this.setScriptSessionFlag(user.getUsername());
// 获得WEB上下文
WebContext wctx = WebContextFactory.get();
//获得在线列表 页面的所有脚本session
Collection sessions = wctx.getScriptSessionsByPage("/ichat/pages/main/online_list.jsp");
Util util = new Util(sessions);
//处理这些页面中的一些元素
//util.addFunctionCall("cBack_list", sb.toString());
util.removeAllOptions("users");
util.addOptions("users", users, "username");
}
/**
* 将用户id和页面脚本session绑定
* @param userid
*/
public void setScriptSessionFlag(String userid) {
WebContextFactory.get().getScriptSession().setAttribute("userid", userid);
}
/**
* 根据用户id获得指定用户的页面脚本session
* @param userid
* @param request
* @return
*/
@SuppressWarnings("unchecked")
public ScriptSession getScriptSession(String userid, HttpServletRequest request) {
ScriptSession scriptSessions = null;
Collection<ScriptSession> sessions = new HashSet<ScriptSession>();
sessions.addAll(ServerContextFactory.get(request.getSession().getServletContext())
.getScriptSessionsByPage("/chat/index.jsp"));
for (ScriptSession session : sessions) {
String xuserid = (String) session.getAttribute("userid");
if (xuserid != null && xuserid.equals(userid)) {
scriptSessions = session;
}
}
return scriptSessions;
}
/**
* 发送消息
* @param sender 发送人
* @param msg 发送内容
* @param request 发送请求
*/
public void send(String sender,String msg,HttpServletRequest request){
System.out.println("sender="+sender+" msg="+msg);
LinkedList<Message> messages = new LinkedList<Message>();
if (msg != null && msg.trim().length() > 0) {
//AA 说(2010-07-13): <br/>你好!
messages.addFirst(new Message(sender,msg));
while (messages.size() > 3) {
messages.removeLast();
}
}
// 获得WEB上下文
WebContext wctx = WebContextFactory.get();
//向指定页面推送消息
Collection sessions = wctx.getScriptSessionsByPage("/ichat/pages/main/show_msg.jsp");
Util utilAll = new Util(sessions);
utilAll.addFunctionCall("callBack", messages);
}
//获得离线消息,思路:当A发消息给B时,将A用户发送的消息保存到B用户的ScriptSession中,
//当B用户上线时把已经保持在B的ScriptSession中的消息读取处理并全部推送到页面。
public void getOfflineMsg(){
}
}
Message.java 消息封装类
package com.ccic.chat;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.directwebremoting.Security;
public class Message {
DateFormat df = new SimpleDateFormat("HH:mm:ss");
private String serverTime = df.format(new Date());
private String text;
private String online;
public String getOnline() {
return online;
}
public void setOnline(String online) {
this.online = online;
}
public Message(String sender,String newtext) {
text = newtext;
if (text.length() > 256) {
text = text.substring(0, 256);
}
text = sender+" 说 "+"("+serverTime+"):<br/>"+text;
}
public String getText() {
return text;
}
}
OnlineCounter.java 在线人数计算方法
package com.ccic.chat;
public class OnlineCounter {
private static long online = 0;
public static long getOnline() {
return online;
}
public static void raise() {
online++;
}
public static void reduce() {
online--;
}
}
OnlineCounterListener.java 统计在线人数
package com.ccic.chat;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
/**
* 统计在线用户数量
*
*/
public class OnlineCounterListener implements HttpSessionListener{
/**
* 当SESSION创建时,自动增加
*/
public void sessionCreated(HttpSessionEvent hse) {
OnlineCounter.raise();
}
/**
* 当SESSION销毁时,自动减少
*/
public void sessionDestroyed(HttpSessionEvent hse){
OnlineCounter.reduce();
}
}
User.java 用户bean
package com.ccic.chat;
/**
* 用户
*
*
*
*/
public class User {
private String password;
private String username;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public User(String password, String username) {
super();
this.password = password;
this.username = username;
}
}
index.jsp --输入http:127.0.0.1:8080/ichat自动访问此页面
<%@ page language="java" contentType="text/html; charset=GBK"
pageEncoding="GBK"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<title>Insert title here</title>
<script language="javascript" type="text/javascript">
function subm(){
var uname=document.form1.username.value;
var pwd=document.form1.password.value;
if (uname == "") {
alert("请输入用户ID");
return;
}
window.location="<%=request.getContextPath() %>/pages/frames/ShowModel_Frames.jsp?uname="+uname+"&pwd="+pwd;
}
</script>
</head>
<body>
<form id="ff" name="form1">
<fieldset>
<legend>登录</legend>
<table align="center">
<tr>
<td>username:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>password:</td>
<td><input type="text" name="password"></td>
</tr>
<tr align="center">
<td colspan="2"><input type="button" name="sub" value="登录" οnclick="subm();"><input type="reset" name="res" value="重置"></td>
</tr>
</table>
</fieldset>
</form>
</body>
</html>
ShowModel_Frames.jsp --登录之后群聊天的主界面
<%@ page language="java" pageEncoding="GB2312"%>
<%
//(模拟)获取登录的唯一用户ID,放到SESSION供online_list.jsp加载在线用户列表
String uname = request.getParameter("uname");
uname=new String(uname.getBytes("ISO-8859-1"),"gbk");
String pwd = request.getParameter("pwd");
request.getSession().setAttribute("uname",uname);
request.getSession().setAttribute("pwd",pwd);
%>
<html>
<head>
<meta HTTP-EQUIV="Pragma" CONTENT="no-cache">
<title>聊天主界面框架</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
</head>
<frameset cols="*,230" frameborder="yes" border="0" framespacing="0">
<frameset rows="400,*" frameborder=yes border="5" framespacing="5">
<frame src="<%=request.getContextPath() %>/pages/main/show_msg.jsp" name="main_one" scrolling=yes noresize>
<frame src="<%=request.getContextPath() %>/pages/main/excute_sent.jsp" name="main_two" scrolling=yes noresize>
</frameset>
<frame src="<%=request.getContextPath() %>/pages/main/online_list.jsp" name="main_top" scrolling=no noresize>
</frameset>
<noframes>
<body>
很抱谦,您使用的浏览器不支持框架功能,请采用新版本的浏览器。
</body>
</noframes>
</html>
example.jsp --FCKEDIT编辑器页面
excute_sent.jsp -- 消息发送页面
<%@ page language="java" contentType="text/html; charset=GBK"
pageEncoding="GBK"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Insert title here</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<script type="text/javascript" src="<%=request.getContextPath() %>/fckeditor/fckeditor.js"></script>
<script type='text/javascript' src='/ichat/dwr/interface/ChatManager.js'></script>
<script type='text/javascript' src='/ichat/dwr/engine.js'></script>
<script type='text/javascript' src='/ichat/dwr/util.js'></script>
<script language="javascript">
/**
* 发送消息
*/
function send() {
var sender = '<%=request.getSession().getAttribute("uname").toString() %>'; // 获得发送者名字
//var receiver = dwr.util.getValue('receiver'); // 获得接受者id
var msg = getEditorHTMLContents('edt1'); // 获得消息内容
ChatManager.send(sender, msg); // 发送消息
SetEditorContents('edt1','');//清空编辑器中发送的消息
}
//获取编辑器中HTML内容
function getEditorHTMLContents(EditorName)
{
var oEditor = FCKeditorAPI.GetInstance(EditorName);
return oEditor.GetXHTML(true);
}
//设置编辑器中内容
function SetEditorContents(EditorName, ContentStr)
{
var oEditor = FCKeditorAPI.GetInstance(EditorName) ;
oEditor.SetHTML(ContentStr) ;
}
</script>
</head>
<body style="margin: 0px;">
<form method="post" name="frm1">
<script type="text/javascript">
var oFCKeditor = new FCKeditor("edt1");
oFCKeditor.BasePath = "<%=request.getContextPath() %>/fckeditor/";
oFCKeditor.ToolbarSet="ichat";
oFCKeditor.Height='160';
oFCKeditor.Value="";
oFCKeditor.Create();
</script>
<input type="button" value="发 送" οnclick="send();">
</form>
</body>
</html>
online_list.jsp --在线列表页面
<%@ page language="java" contentType="text/html; charset=GBK"
pageEncoding="GBK" import="com.ccic.chat.OnlineCounter" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Insert title here</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<link rel="stylesheet" href="<%=request.getContextPath()%>/include/css/dtree.css" type="text/css">
<script src="<%=request.getContextPath()%>/include/js/dtree.js"></script>
<script type='text/javascript' src='/ichat/dwr/interface/ChatManager.js'></script>
<script type='text/javascript' src='/ichat/dwr/engine.js'></script>
<script type='text/javascript' src='/ichat/dwr/util.js'></script>
</head>
<script language="javascript">
function register() {
//把我输入的用户名注册到服务器
ChatManager.updateUsersList();
}
//页面初始化
function init() {
dwr.engine.setActiveReverseAjax(true); // 激活反转 重要
register();
}
//回调函数
//function cBack_list(data){
//document.getElementById("online_list").insertAdjacentHTML("afterBegin","");
//var str="aa"+data+">";
//document.getElementById("online_list").insertAdjacentHTML("afterBegin",str);
//}
</script>
<body οnlοad="init();">
在线用户列表(当前<%=OnlineCounter.getOnline() %>人):
<ul id="users">
</ul>
<!--<div id="online_list">
</div>
-->
</body>
</html>
show_msg.jsp --显示消息页面
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Insert title here</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<script type='text/javascript' src='/ichat/dwr/interface/ChatManager.js'></script>
<script type='text/javascript' src='/ichat/dwr/engine.js'></script>
<script type='text/javascript' src='/ichat/dwr/util.js'></script>
</head>
<script language="javascript">
//页面初始化
function init() {
dwr.engine.setActiveReverseAjax(true); // 激活反转 重要
}
function callBack(data){
DWRUtil.addRows("tbodyId", data, cellFunctions,{escapeHtml:false});
}
var cellFunctions = [
function(item) {
return item.text;
}
];
</script>
<body οnlοad="init();">
<table>
<tbody id="tbodyId">
</tbody>
</table>
</body>
</html>
dwr.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 2.0//EN" "http://getahead.org/dwr/dwr20.dtd"> <dwr> <allow> <convert converter="bean" match="com.ccic.chat.User"/> <create creator="new" javascript="ChatManager"> <param name="class" value="com.ccic.chat.ChatManager"/> </create> <convert converter="bean" match="com.ccic.chat.Message"/> </allow> </dwr>
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>ichat</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <servlet> <servlet-name>dwr-invoker</servlet-name> <servlet-class> org.directwebremoting.servlet.DwrServlet </servlet-class> <init-param> <description>调试DWR,发布系统时应将其设为false</description> <param-name>debug</param-name> <param-value>true</param-value> </init-param> <init-param> <description>使用服务器推技术(反转AJAX)</description> <param-name>activeReverseAjaxEnabled</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name> initApplicationScopeCreatorsAtStartup </param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>maxWaitAfterWrite</param-name> <param-value>100</param-value> </init-param> <load-on-startup>4</load-on-startup> </servlet> <listener> <listener-class> com.ccic.chat.OnlineCounterListener </listener-class> </listener> <servlet-mapping> <servlet-name>dwr-invoker</servlet-name> <url-pattern>/dwr/*</url-pattern> </servlet-mapping> </web-app>
关于FCKEDIT编辑器,我已经删除多余的一些东西了,收集:
----------------------FCKEDIT HTML在线编辑器------------------------------
配置:http://blog.youkuaiyun.com/xiaokuang513204/archive/2010/07/06/5715029.aspx
插件开发:http://blog.youkuaiyun.com/flying_huang/archive/2007/03/23/1539206.aspx
火狐 兼容fckedt:http://www.wangzhanweb.com/html/2010-05/231.html
FCKEditor的赋值和取值操作 :http://www.blogjava.net/feingto/archive/2008/01/09/173963.html
我参考的网上两个例子,见附件!名称“chat”
一个是所有对象间聊天的,访问时对应得页面是index.jsp.
一个是点对点对象聊天的,访问时对应得页面是sample.jsp.
两者都只是简单的例子实现。
自己实现的群聊天简单例子,见附件!名称“ichat”
运行:输入http://127.0.0.1:8080/ichat/ 进入登录界面,输入“AA” 或者别的就行了。
参考:
开源的comet实现:pushlet,dwr 2.0的reverse ajax和dojo的io.bind(),
简单例子(reverse ajax):http://blog.sina.com.cn/s/blog_5bd96d520100gau4.html
谈谈webIM :http://akalius.iteye.com/blog/192727