用dwr的comet(推)来实现简单的无刷新多人聊天室,comet是长连接的一种。通常我们要实现无刷新,一般会使用到Ajax。Ajax 应用程序可以使用两种基本的方法解决这一问题:一种方法是浏览器每隔若干秒时间向服务器发出轮询以进行更新,另一种方法是服务器始终打开与浏览器的连接并在数据可用时发送给浏览器。第一种方法一般利用setTimeout或是setInterval定时请求,并返回最新数据,这无疑增加了服务器的负担,浪费了大量的资源。而第二种方法也会浪费服务器资源,长期的建立连接;而相对第一种来说,第二种方式会更优于第一种方法;这里有一个一对多和多对一的关系,而comet向多个客户端推送数据就是一对多的关系。而具体使用哪种方式,要看你当前的需求而定,没有绝对的。

为什么使用 Comet?

轮询方法的主要缺点是:当扩展到更多客户机时,将生成大量的通信量。每个客户机必须定期访问服务器以检查更新,这为服务器资源添加了更多负荷。最坏的一种情况是对不频繁发生更新的应用程序使用轮询,例如一种 Ajax 邮件 Inbox。在这种情况下,相当数量的客户机轮询是没有必要的,服务器对这些轮询的回答只会是 “没有产生新数据”。虽然可以通过增加轮询的时间间隔来减轻服务器负荷,但是这种方法会产生不良后果,即延迟客户机对服务器事件的感知。当然,很多应用程序可以实现某种权衡,从而获得可接受的轮询方法。

尽管如此,吸引人们使用 Comet 策略的其中一个优点是其显而易见的高效性。客户机不会像使用轮询方法那样生成烦人的通信量,并且事件发生后可立即发布给客户机。但是保持长期连接处于打开状态也会消耗服务器资源。当等待状态的 servlet 持有一个持久性请求时,该 servlet 会独占一个线程。这将限制 Comet 对传统 servlet 引擎的可伸缩性,因为客户机的数量会很快超过服务器栈能有效处理的线程数量。

如果本示例结合Jetty应用服务器效果会更好。

一、准备工作

1、 下载dwr的相关jar包

https://java.net/downloads/dwr/Development%20Builds/Build%20116/dwr.jar

程序中还需要spring的相关jar包

http://ebr.springsource.com/repository/app/library/version/detail?name=org.springframework.spring&version=3.0.5.RELEASE

需要的jar包如下

2、 建立一个WebProject,名称DWRComet

在web.xml中添加dwr、spring配置如下:

<span style="font-size:18px;"><?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"
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"> <!-- 加载Spring容器配置 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 设置Spring容器加载配置文件路径 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:applicationContext-*.xml</param-value> </context-param> <listener> <listener-class>org.directwebremoting.servlet.DwrListener</listener-class> </listener> <servlet> <servlet-name>dwr-invoker</servlet-name> <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>true</param-value> </init-param> <!-- dwr的comet控制 --> <init-param> <param-name>pollAndCometEnabled</param-name> <param-value>true</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>dwr-invoker</servlet-name> <url-pattern>/dwr/*</url-pattern> </servlet-mapping> <welcome-file-list>
<welcome-file>chat.jsp</welcome-file>
</welcome-file-list>
</web-app></span>

3、 在src目录加入applicationContext-beans.xml配置,这个配置专门配置bean对象,用来配置需要注入的对象。

<span style="font-size:18px;"><?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"

	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util"

	xmlns:context="http://www.springframework.org/schema/context"

	xsi:schemaLocation="http://www.springframework.org/schema/beans 

    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

    http://www.springframework.org/schema/aop 

    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd

    http://www.springframework.org/schema/tx 

    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd

    http://www.springframework.org/schema/util 

    http://www.springframework.org/schema/util/spring-util-3.0.xsd

    http://www.springframework.org/schema/context 

    http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="chatService" class="com.tgb.chat.ChatService" /> <bean id="chatMessageClient" class="com.tgb.chat.ChatMessageClient" /> </beans></span>

4、 在WEB-INF目录添加dwr.xml文件,基本代码如下

<span style="font-size:18px;"><?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 3.0//EN" "http://getahead.org/dwr/dwr30.dtd">
<dwr>
</dwr></span>

以上的准备基本完毕,下面来完成无刷新聊天室代码

二、聊天室相关业务实现

1、 聊天实体类Model

<span style="font-size:18px;">package com.tgb.entity;

import java.util.Date;

public class Message {

	private int id;
private String msg;
private Date time;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
} }
</span>

2、 编写聊天信息的事件

<span style="font-size:18px;">package com.tgb.chat;

import org.springframework.context.ApplicationEvent;

public class ChatMessageEvent extends ApplicationEvent {
private static final long serialVersionUID = 1L; public ChatMessageEvent(Object source) { super(source); } }
</span>

继承ApplicationEvent,构造参数用于传递发送过来的消息。这个事件需要一个监听器监听,一旦触发了这个事件,我们就可以向客户端发送消息。

3、 发送消息服务类,用户客户端发送消息。dwr需要暴露这个类里面的发送消息的方法

<span style="font-size:18px;">package com.tgb.chat;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEventPublisher; import com.tgb.entity.Message; public class ChatService implements ApplicationContextAware {
private ApplicationContext ctx; public void setApplicationContext(ApplicationContext ctx) throws BeansException { this.ctx = ctx; } /** * <b>function:</b> 向服务器发送信息,服务器端监听ChatMessageEvent事件,当有事件触发就向所有客户端发送信息 * @author hoojo * @createDate 2011-6-8 下午12:37:24 * @param msg */ public void sendMessage(Message msg) { //发布事件 ctx.publishEvent(new ChatMessageEvent(msg)); } public void Init(){ }
}
</span>

上面的sendMessage需要浏览器客户端调用此方法完成消息的发布,传递一个Message对象,并且是触发ChatMessageEvent事件。

4、 编写监听器监听客户端是否触发ChatMessageEvent

<span style="font-size:18px;">package com.tgb.chat;

import java.util.Collection;
import java.util.Date; import javax.servlet.ServletContext; import org.directwebremoting.ScriptBuffer;
import org.directwebremoting.ScriptSession;
import org.directwebremoting.ServerContext;
import org.directwebremoting.ServerContextFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.web.context.ServletContextAware; import com.tgb.entity.Message; @SuppressWarnings("unchecked")
public class ChatMessageClient implements ApplicationListener,ServletContextAware{
private ServletContext ctx; public void setServletContext(ServletContext ctx) { this.ctx = ctx; } @SuppressWarnings("deprecation") public void onApplicationEvent(ApplicationEvent event) { //如果事件类型是ChatMessageEvent就执行下面操作 if (event instanceof ChatMessageEvent) { Message msg = (Message) event.getSource(); ServerContext context = ServerContextFactory.get(); //获得客户端所有chat页面script session连接数 Collection<ScriptSession> sessions = context.getScriptSessionsByPage(ctx.getContextPath() + "/chat.jsp"); for (ScriptSession session : sessions) { ScriptBuffer sb = new ScriptBuffer(); Date time = msg.getTime(); String s = time.getYear() + "-" + (time.getMonth() + 1) + "-" + time.getDate() + " " + time.getHours() + ":" + time.getMinutes() + ":" + time.getSeconds(); //执行setMessage方法 sb.appendScript("showMessage({msg: '") .appendScript(msg.getMsg()) .appendScript("', time: '") .appendScript(s) .appendScript("'})"); System.out.println(sb.toString()); //执行客户端script session方法,相当于浏览器执行JavaScript代码 //上面就会执行客户端浏览器中的showMessage方法,并且传递一个对象过去 session.addScript(sb); } } } }
</span>

上面的代码主要是监听客户端的事件,一旦客户端有触发ApplicationEvent事件或是其子类,就会执行onApplicationEvent方法。代码中通过instanceof判断对象实例,然后再执行。如果有触发ChatMessageEvent事件,就获取所有连接chat.jsp这个页面的ScriptSession。然后像所有的ScriptSession中添加script。这样被添加的ScriptSession就会在有连接chat.jsp的页面中执行。

所以这就是客户端为什么会执行服务器端的JavaScript代码。但前提是需要在web.xml中添加dwrComet配置以及在chat页面添加ajax反转。

5、 下面开始在bean容器和dwr的配置中添加我们的配置

applicationContext-beans.xml配置

<span style="font-size:18px;"><bean id="chatService" class="com.hoo.chat.ChatService"/>
<bean id="chatMessageClient" class="com.hoo.chat.ChatMessageClient"/></span>

上面的chatService会在dwr配置中用到

dwr.xml配置

<span style="font-size:18px;"><allow>
<convert match="com.hoo.entity.Message" converter="bean">
<param name="include" value="msg,time" />
</convert> <create creator="spring" javascript="ChatService">
<param name="beanName" value="chatService" />
</create>
</allow></span>

charService的sendMessage方法传递的是Message对象,所以要配置Message对象的convert配置。

上面的create的creator是spring,表示在spring容器中拿chatService对象。里面的参数的beanName表示在spring容器中找name等于charService的bean对象。

6、 客户端chat.jsp页面代码

<span style="font-size:18px;"><%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>"> <title>微信内部聊天</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"> <script type="text/javascript"
src="${pageContext.request.contextPath }/dwr/engine.js"></script>
<script type="text/javascript"
src="${pageContext.request.contextPath }/dwr/util.js"></script>
<script type="text/javascript"
src="${pageContext.request.contextPath }/dwr/interface/ChatService.js"></script>
<script type="text/javascript">
function send() {
var time = new Date();
var content = dwr.util.getValue("content");
var name = dwr.util.getValue("userName");
var info = encodeURI(encodeURI(name + " 说:\n" + content));
var msg = {
"msg" : info,
"time" : time
};
dwr.util.setValue("content", "");
if (!!content) { ChatService.sendMessage(msg); } else {
alert("发送的内容不能为空!");
}
}
function showMessage(data) {
var message = decodeURI(decodeURI(data.msg));
var text = dwr.util.getValue("info");
if (!!text) {
dwr.util.setValue("info", text + "\n" + data.time + " " + message);
} else {
dwr.util.setValue("info", data.time + " " + message);
}
}
</script>
<script type="text/javascript">
window.onload = Init()
function Init() {
ChatService.Init();
alert("页面初始化加载成功啦");
}
</script>
</head> <body onload="dwr.engine.setActiveReverseAjax(true);">
<div id="info"
style="margin-top: 3px; padding-top: 3px; height: 89%; overflow-y: auto; border: 1px solid #000000;"></div>
<!-- <textarea rows="20" cols="60" id="" readonly="readonly"></textarea> -->
<hr /> <input type="text" id="userName" /> <div style="margin-bottom: 10px; height: 10%">
<input id="content" type="text" style="height: 45px; width: 74%"><input
type="button" value="发送" style="height: 48px; width: 25%"
onclick="send();">
</div> <!-- 原本的发送和输入消息框 -->
<!-- <textarea rows="5" cols="30" id="content"></textarea> -->
<!-- <input type="button" value=" Send " onclick="send()" -->
<!-- style="height: 85px; width: 85px;" /> -->
</body>
</html>
</span>

首先,你需要导入dwr的engine.js文件,这个很重要,是dwr的引擎文件。其次你使用的那个类的方法,也需要在导入进来。一般是interface下的,并且在dwr.xml中配置过的create。

上面的js中调用的charService类中的sendMessage方法,所以在jsp页面中导入的是ChatService.js。

在body的onload事件中,需要设置反转Ajax,这个很重要。

showMessage是ChatMessageClient的onApplicationEvent方法中的appendScript中需要执行的方法。data参数也是在那里传递过来的。

每当发送sendMessage方法后就会触发ChatMessageEvent事件,然后监听的地方就会执行onApplicationEvent方法,在这个方法中又会执行浏览器中的showMessage方法。

Spring整合DWR comet 实现无刷新 多人聊天室的更多相关文章

  1. 基于JQuery+JSP的无数据库无刷新多人在线聊天室

    JQuery是一款非常强大的javascript插件,本文就针对Ajax前台和JSP后台来实现一个无刷新的多人在线聊天室,该实现的数据全部存储在服务端内存里,没有用到数据库,本文会提供所有源程序,需要 ...

  2. spring mvc + xmlHttpRequest2.0 实现无刷新上传文件,带进度条和剩余时间

    1.springmvc支持文件上传,需要在spring-mvc.xml配置文件中加上下面的一段话: <!-- 支持上传文件 --> <bean id="multipartR ...

  3. 整合pjax无刷新

    一:整合pjax的准备工作: 检查你的网站是否引入1.7.0版本以上的jquery.js,如果没有请全局引入 1.新浪CDN提速:<script type="text/javascri ...

  4. 二 SSH整合:Spring整合Hibernate,无障碍整合&无核心配置整合,Hibernate模版常用方法,

    重建SSH项目 java项目可以直接复制,但是web项目除了改名字还要该配置,如下: 方式一:无障碍整合:带Hibernate配置文件 <?xml version="1.0" ...

  5. jQuery AJAX 网页无刷新上传示例

    新年礼,提供简单.易套用的 jQuery AJAX 上传示例及代码下载.后台对文件的上传及检查,以 C#/.NET Handler 处理 (可视需要改写成 Java 或 PHP). 有时做一个网站项目 ...

  6. Mybatis与Spring整合,使用了maven管理项目,作为初学者觉得不错,转载下来

    转载自:http://www.cnblogs.com/xdp-gacl/p/4271627.html 一.搭建开发环境 1.1.使用Maven创建Web项目 执行如下命令: mvn archetype ...

  7. Hibernate注解使用以及Spring整合

    Hibernate注解使用以及Spring整合 原文转自:http://wanqiufeng.blog.51cto.com/409430/484739 (1) 简介: 在过去几年里,Hibernate ...

  8. Spring整合jdbc

    首先web.xml文件跟往常一样,加载spring容器和加载org.springframework.web.context.ContextLoaderListener读取applicationCont ...

  9. Struts2+Hibernate+Spring 整合示例[转]

    原文 http://blog.csdn.net/tkd03072010/article/details/7468769 Spring整合Struts2.Hibernate原理概述: 从用户角度来看,用 ...

随机推荐

  1. Shiro整合Spring

    首先需要添加shiro的spring整合包. 要想在WEB应用中整合Spring和Shiro的话,首先需要添加一个由spring代理的过滤器如下: <!-- The filter-name ma ...

  2. codefroces 911G Mass Change Queries

    题意翻译 给出一个数列,有q个操作,每种操作是把区间[l,r]中等于x的数改成y.输出q步操作完的数列. 输入输出格式 输入格式: The first line contains one intege ...

  3. 【IOI1998】Picture(扫描线+线段树)

    问题来源:IOI1998 D2T1 题意:就是在一个平面内给出n个矩形,叫你计算将这些矩形合并以后,新图形的周长. 例如: 上图是原本的矩形们 ---------->合并后的图形 解题思路:拿一 ...

  4. ●POJ 1556 The Doors(简单计算几何+最短路)

    ●赘述题目 10*10的房间内,有竖着的一些墙(不超过18个).问从点(0,5)到(10,5)的最短路. 按照输入样例,输入的连续5个数,x,y1,y2,y3,y4,表示(x,0--y1),(x,y2 ...

  5. 作为开发也要了解的 mysql 优化思路

    作为开发人员,数据库知识掌握的可能不是很深入,但是一些基本的技能还是要有时间学习一下的.作为一个数据库菜鸟,厚着脸皮来总结一下 mysql 的基本的不能再基本的优化方法. 为了更好的说明,我假想出来了 ...

  6. Automap sqlalchemy.ext.automap 自动映射数据库表结构

    from sqlalchemy.ext.automap import automap_base from sqlalchemy.orm import Session from sqlalchemy i ...

  7. 利用maven install jar到项目当中

    接着上面利用maven打好的jar包.把刚刚打好的包放入其他项目当中怎么办? 只需要在相同的目录下执行mvn install,maven会自动把jar放到本地仓库中. 这样,原先maven项目中缺少依 ...

  8. Windows 下 Ionic 开发环境搭建

    Ionic 介绍 首先,Ionic 是什么. Ionic 是一款基于 Cordova 及 Angular 开发 Hybrid/Web APP 的前端框架,类似的其他框架有:Intel XDK等. 简单 ...

  9. ubuntu14.04 64位 安装H3C iNode客户端

    环境: OS: ubuntu14.04LTS 64位 iNode:  iNode2.40-R0162 for linux(iNode只有32位的,而且是很久以前的版本) 安装方法: 第一种: 主要参考 ...

  10. ubuntu14.04 python2.7 安装配置OpenCV3.0

    环境:ubuntu14.04  python2.7 内容:安装并配置OpenCV3.0 今天按照OpenCV官网上的步骤装了OpenCV但是,装好之后python提示“No module named ...