b/s模式下的即时通讯,使用ajax框架dwr实现
了解java的发展史可以知道,客户端编程在基于浏览器的编程方面,以前的做法是用applet实现客户端编程,在当时算是流行的做法,但是随着IE的不一致,尤其是微软的不支持,
Applet没有发展起来,还有一个原因就是在浏览器中要下载java运行时插件,这几M的大小,对于以前网速就慢的网络,无疑断送了它的性命。现在应用与客户端浏览器的技术主要为一些牛人自己开发的插件,通过下载实现自己的功能。现在最通用最新的做法是利用XmlHttpRequext,异步实现客户端请求,也就是通常所说的ajax(异步的JavaScript和xml)技术,可以使用封装好的dwr框架简化开发,另一种方式就是使用Flex技术,就是Flash脚本编程,只是听说,自己还没有用过。
自己写了一个使用ajax框架dwr实现简易的即时通信程序。
首先什么是dwr呢?DWR是一个框架,简单的说就是能够在javascript直接调用java方法,而不必去写一大堆的javascript代码。它的实现是基于ajax的,可以实现无刷新效果。
一、 dwr配置篇
1.web.xml
<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>
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
这个参数DWR默认是false。如果选择true,我们可以通过 http://localhost:port/app/dwr看到你部署的每个DWR class。并且可以测试java代码的每个方法是否运行正常。为了安全考虑,在正式环境下你一定把这个参数设为false。
2.使用dwr还要由一个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.lhq.User" />
<create creator="new" javascript="ChatManager">
<param name="class" value="com.lhq.ChatManager" />
</create>
</allow>
</dwr>
二.Dwr使用篇
实例:b/s模式下的即时通讯,使用ajax框架dwr实现
1. 首先把dwr.jar包放在项目lib目录下。
2. 进行上述web.xml相关配置,上述配置就是本实例的配置。
3. 为实现功能写的javabean,可能还有servlet,但本实例没有牵涉到servlet。代码如下:
package com.lhq;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import org.directwebremoting.ScriptBuffer;
import org.directwebremoting.ScriptSession;
import org.directwebremoting.ServerContext;
import org.directwebremoting.ServerContextFactory;
import org.directwebremoting.WebContextFactory;
import org.directwebremoting.proxy.dwr.Util;
/**
* 处理聊天相关
*
* @author lhq
*
*/
public class ChatManager {
/** 保存当前在线用户列表 */
public static List<User> users = new ArrayList<User>();
/**
* 更新在线用户列表
*
* @param username
* 待添加到列表的用户名
* @param flag
* 是添加用户到列表,还是只获得当前列表
* @param request
* @return 用户userid
*/
public String updateUsersList(String username, boolean flag,
HttpServletRequest request) {
User user = null;
if (flag) {
// 这里取会话(HttpSession)的id为用户id
System.out.println("aauserid:" + request.getSession().getId());
user = new User(request.getSession().getId(), username);
// 保存用户到列表
users.add(user);
// 将用户id和页面脚本session绑定
this.setScriptSessionFlag(user.getUserid());
}
// 获得DWR上下文
ServletContext sc = request.getSession().getServletContext();
ServerContext sctx = ServerContextFactory.get(sc);
// 获得当前浏览 index.jsp 页面的所有脚本session
Collection sessions = sctx
.getScriptSessionsByPage("/BalanceCenter/index.jsp");
System.out.println("session的记录数:" + sessions.size());
// 消除不存在的页面session
System.out.println("session的记录数(消除后):" + sessions.size());
Util util = new Util(sessions);
// 处理这些页面中的一些元素
util.removeAllOptions("users");
util.addOptions("users", users, "username");
util.removeAllOptions("receiver");
util.addOptions("receiver", users, "userid", "username");
if (!flag) {
return null;
}
return user.getUserid();
}
/**
*
* 当退出时更新user
*
*
*/
public void delUser(HttpServletRequest request) {
String userid = request.getSession().getId();// 与userid相等,呵呵
System.out.println("userIdaa:" + userid);
Iterator it = users.iterator();
while (it.hasNext()) {
User user = (User) it.next();
System.out.println(user.getUsername());
System.out.println("userId:" + user.getUserid());
if (user.getUserid().equals(userid)) {
users.remove(user);
break;
}
}
updateUsersList(null, false,request) ;
}
/**
* 将用户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("/BalanceCenter/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 receiverid
* 接收者id
* @param msg
* 消息内容
* @param request
*/
public void send(String sender, String receiverid, String msg,
HttpServletRequest request) {
ScriptSession session = this.getScriptSession(receiverid, request);
Util util = new Util(session);
util.setStyle("showMessage", "display", "");
ScriptBuffer script = new ScriptBuffer();
script.appendScript("receiveMessages(").appendData(
sender + "说:" + msg + "/n").appendScript(");");
session.addScript(script);
}
}
4. 进行dwr.xml相对于javabean的相关配置,及提供客户端JavaScript脚本对于java方法的可见性。上述代码及为本实例代码。
5. 客户端javascript中调用
自定义一个chat.js文件,用来跟后台进行交互。代码如下:
/**
* 注册
*/
function register(button) {
if ($('username').value == "" || $('username').value.length <= 0) {
alert("请输入昵称");
return;
}
/*相应按钮处理 */
$('username').disabled = true;
button.disabled = true;
//$('message').disabled = false;
$('send').disabled = false;
$('receiver').disabled = false;
$('send').disabled = false;
$('exit').disabled = false;
/*调用后台ChatManager类的updateUsersList方法,并取得返回值元数据放在data中*/
ChatManager.updateUsersList($('username').value, true, function(data) {
if (data != null && data.length > 0) {
$('userid').value = data; // 把当前sessionId放在隐藏表单
}
});
}
/**
*初始化
*/
function init() {
dwr.engine.setActiveReverseAjax(true); // 激活反转
ChatManager.updateUsersList(null, false); //和上面那个方法一样
}
function delUser(button) {
ChatManager.delUser(); //当退出是调用,消除该在线用户
/**
*并对相应按钮进行相应处理
*/
$('username').disabled = false;
$('register').disabled = false;
button.disabled=true;
$('receiver').disabled = true;
$('send').disabled = true;
$('areaMessage').innerHTML = "";
}
/**
* 当发送信息是调用
*/
function send() {
var sender = dwr.util.getValue('username'); //取得该用户的名字
var receiver = dwr.util.getValue('receiver'); // 取得接收者
var msg = FCKeditorAPI.GetInstance("message").EditorDocument.body.innerHTML; // 取得文本编辑器的文本
ChatManager.send(sender, receiver, msg); //调用后台方法
}
/**
*这是后台推技术调用的方法,接受信息
*/
function receiveMessages(message) {
//取得聊天信息中文本
var messages=document.getElementById("areaMessage").innerHTML;
document.getElementById("areaMessage").innerHTML=message+"<hr/>"+messages;
}
window.onload = init;//页面加载时
6. html代码
需要导入必须的js文件,在文本编辑时,我用了一个fckEditor文本编辑器,上网下一个就可以了,放在根目录下,本人美感欠缺,页面不太好看,请见谅!具体代码如下:
<%@ page language="java" pageEncoding="GBK"%>
<% String context=request.getContextPath();
pageContext.setAttribute("ctx",context);
%>
<html>
<head>
<title>chat</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='/BalanceCenter/dwr/interface/ChatManager.js'></script>
<script type='text/javascript' src='/BalanceCenter/dwr/engine.js'></script>
<script type='text/javascript' src='/BalanceCenter/dwr/util.js'></script>
<script type="text/javascript" src="/BalanceCenter/chat.js"></script>
<script type="text/javascript" src="${ctx}/fckeditor/fckeditor.js"></script>
</head>
<body>
<input type="hidden" name="userid" />
<br>
昵称:
<input type="text" name="username" id="username" />
<input type="button" value="注册" onclick="register(this);" id="register"/>
<input type="button" value="退出" onclick="delUser(this);" id="exit"/>
<br />
<br />
我要对
<select name="receiver" id="receiver" disabled=true" >
</select>
说:
<script type="text/javascript">
var oFCKeditor = new FCKeditor("message");
oFCKeditor.BasePath = '${ctx}/fckeditor/' ;
oFCKeditor.Height = 300 ;
oFCKeditor.ToolbarSet = 'Default';
oFCKeditor.Create() ;
</script>
<input type="button" value="发送" id="send" name="send" disabled="true"
onclick="send();" />
<br />
<br />
在线用户列表:
<ul id="users">
</ul>
<div id="showMessage" style="display: none">
<div id="areaMessage" style="position:absolute;
width:600px;
height:300px;
z-index:1;
border:solid;
overflow:auto;
">
</div>
</div>
</body>
</html>
好了,终于写完了,首先要说明一下,本程序不是很完善,具体在:用户只有点击“退出”按钮才可以实现把该在线人员删除掉,可能利用js也可以实现关闭页面也可以把该在线人员删除掉,但是如果用户刷新了,怎么办呢???就是怎么记录有效地scriptsession呢?有人说用map保存。本人没有试过,还望高手指点,不胜感激!!!