1. 实现原理
当主页加载完毕时候,一个ajax(jquery)请求到servlet端, servlet启动异步处理上下文AsyncContext,然后请求一直等待,直到上下文AsyncContext调用complete()方法,或者这个请求timeout,这个请求才会返回到UI
,这样一次连接就结束。请求返回到UI后,ajax马上又发送连接请求到Servlet,这样反反复复进行这个操作
2. 实现代码
Servlet端实现
//需要导入该类包名
import java.io.IOException;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@WebServlet(urlPatterns = { "/servlet/asyn" }, asyncSupported = true)
public class AsynServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setCharacterEncoding("UTF-8");
String timeoutStr = request.getParameter("timeout");
long timeout;
if (StringUtils.isNumeric(timeoutStr)) {
timeout = Long.parseLong(timeoutStr);
} else {
// 设置10分钟
timeout = 10 * 60 * 1000;
}
final HttpServletResponse finalResponse = response;
final AsyncContext ac = request.startAsync(request, finalResponse);
// 设置成长久链接
ac.setTimeout(timeout);
ac.addListener(new AsyncListener() {
public void onComplete(AsyncEvent event) throws IOException {
log.info("onComplete Event!");
ServletService.getInstance().removeAsyncContext(ac);
}
public void onTimeout(AsyncEvent event) throws IOException {
log.info("onTimeout Event!");
ServletService.getInstance().removeAsyncContext(ac);
ac.complete();
}
public void onError(AsyncEvent event) throws IOException {
ServletService.getInstance().removeAsyncContext(ac);
ac.complete();
}
public void onStartAsync(AsyncEvent event) throws IOException {
log.info("onStartAsync Event!");
}
});
ServletService.getInstance().addAsyncContext(ac);
}
}
发消息业务方法
//需要导入该类包名
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import javax.servlet.AsyncContext;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class ServletService {
//异步Servlet上下文队列.
private final Map<Integer, AsyncContext> ASYNC_CONTEXT_MAP = new ConcurrentHashMap<Integer, AsyncContext>();
//消息队列.
private final BlockingQueue<TextMessage> TEXT_MESSAGE_QUEUE = new LinkedBlockingQueue<TextMessage>();
//单一实例.
private static ServletService instance = new ServletService();
//构造函数,创建发送消息的异步线程.
private ServletService() {
new Thread(this.notifierRunnable).start();//线程发发消息给多个用户
}
//单一实例.
public static ServletService getInstance() {
return instance;
}
/**
*
* 注册异步Servlet上下文.
*
* @param asyncContext
* 异步Servlet上下文.
*/
public void addAsyncContext(final AsyncContext asyncContext) {
HttpServletRequest req = (HttpServletRequest) asyncContext.getRequest();
User user = (User) req.getSession().getAttribute("loginuser");
if (null!=user) {
ASYNC_CONTEXT_MAP.put(user.getId(), asyncContext);
}
}
/**
*
* 删除异步Servlet上下文.
*
* @param asyncContext
* 异步Servlet上下文.
*/
public void removeAsyncContext(final AsyncContext asyncContext) {
HttpServletRequest req = (HttpServletRequest) asyncContext.getRequest();
User user = (User) req.getSession().getAttribute("loginuser");
if (null!=user) {
ASYNC_CONTEXT_MAP.remove(user.getId());
}
}
/**
*
* 发送消息到异步线程,最终输出到http response 流 .
*
* @param text 发送给客户端的消息.
*
*/
public void putMessage(final int userId, final String text) {
try {
TextMessage tm = new TextMessage(userId, text);
TEXT_MESSAGE_QUEUE.add(tm);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public void putMessage(final TextMessage tm) {
try {
TEXT_MESSAGE_QUEUE.add(tm);
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public boolean pushMessage(final TextMessage tm) {
boolean result = false;
AsyncContext ac = ASYNC_CONTEXT_MAP.get(tm.getUserId());
try {
if (null != ac) {
write(ac, tm.getText());
result = true;
}
} catch (Exception e) {
ASYNC_CONTEXT_MAP.remove(tm.getUserId());
log.info(e);
}
return result;
}
/**
*
* 异步线程,当消息队列中被放入数据,将释放take方法的阻塞,将数据发送到http response流上.
* 该方法暂时没用,用于并发测试
*/
private Runnable notifierRunnable = new Runnable() {
public void run() {
boolean done = false;
while (!done) {
try {
final TextMessage tm = TEXT_MESSAGE_QUEUE.take();//当消息队列没有数据时候,线程执行到这里就会被阻塞
if (tm.getUserId()==0) {//发送给所有人
for (Entry<Integer, AsyncContext> entry : ASYNC_CONTEXT_MAP.entrySet()) {
try {
write(entry.getValue(), tm.getText());
} catch (IOException ex) {
log.info(ex);
}
}
}
else {
pushMessage(tm);
}
Thread.sleep(100);//暂停100ms,停止的这段时间让用户有足够时间连接到服务器
} catch (InterruptedException iex) {
done = true;
log.info(iex);
}
}
}
};
private void write(AsyncContext ac, String text) throws IOException {
PrintWriter acWriter = ac.getResponse().getWriter();
acWriter.write(text);
acWriter.flush();
acWriter.close();
ac.complete();
}
}
发消息实体对象
public final class TextMessage {
private final int userId;
private final String text;
public TextMessage(final int userId, final String text) {
super();
this.userId = userId;
this.text = text;
}
public int getUserId() {
return userId;
}
public String getText() {
return text;
}
}
3. 前端js实现(重点)
主页要引入下面脚本代码,当主页加载完毕后,就会发送ajax请求到servet/asyn.do,然后就建立连接等待servlet返回消息到主页,返回到主页调用showMessage,这个方法调用ext组件弹出消息内容
initServlet();
function initServlet() {
$.ajax({
url:'servlet/asyn',
type:'get',
dataType:'text',
contentType: "text/plain; charset=UTF-8",
timeout:600000,
success: function(msg){
if (msg!=null && msg!='') {//这个返回值要改成json对象
showMessage(msg);
}
callSelf();//把这个方法里面的代码放在这里,IE8会一直死循环的调用(其它浏览器不会),不知道什么原因
}
});
}
function callSelf() {
initServlet();
}
function showMessage(data) {
Ext.create('widget.uxNotification', {
title: '消息提醒',
position: 'br',
cls: 'ux-notification-light',
iconCls: 'ux-notification-icon-information',
html: data,
width: 200,
height: 90,
autoCloseDelay: 4000,
slideBackDuration: 500,
slideInAnimation: 'bounceOut',
slideBackAnimation: 'easeIn'
}).show();
}
//js获取项目根路径,如: localhost:8080/test
function getRootPath(){
//获取当前网址,如: http://localhost:8080/test/test.jsp
var curWwwPath=window.document.location.href;
//获取主机地址之后的目录,如: test/test.jsp
var pathName=window.document.location.pathname;
var pos=curWwwPath.indexOf(pathName);
//获取主机地址,如: http://localhost:8080
var localhostPaht=curWwwPath.substring(0,pos);
var wsPath = localhostPaht.replace('http://','');
//获取带"/"的项目名,如:/test
var projectName=pathName.substring(0,pathName.substr(1).indexOf('/')+1);
return(wsPath+projectName);
}
前端要引用ext右下角弹出消息组件:
Notification extension for Ext JS 4.0.2+
Version: 2.1.3
从这里下载
https://github.com/EirikLorentsen/Ext.ux.window.Notification
分享到:
相关推荐
WebSocket则建立持久连接,允许服务器主动推送新消息给客户端。 在提供的"ChatRoom"源代码中,你可以看到这些概念的具体实现,包括Servlet类、HTML页面和JavaScript脚本。通过分析和运行这个案例,你不仅可以了解...
3. 在服务器端,JSP接收到消息后进行处理,可以将消息保存到数据库或队列中,并触发服务器推送。 4. 实现服务器推送,可以通过Comet技术(如长轮询)或者WebSocket。在这个例子中,考虑到标签提及了“jsp聊天”,...
Servlet 3.0的异步推送(也称为Comet技术)通过在服务器端维持一个打开的连接,可以在有新消息时立即推送到客户端,提高了用户体验。 在这个聊天室示例中,"application.js"文件可能是用于处理前端交互逻辑,如接收...
这部分可能涉及到WebSocket或AJAX异步通信,或者使用其他服务器推送技术。 8. **部署与运行**:项目完成后,需要将所有依赖和Servlet部署到支持Java EE的Web服务器上,如Tomcat或Jetty。用户需要设置好服务器环境,...
例如,当用户发送消息时,Ajax调用后台Servlet处理消息,并将回复推送给其他在线用户。 6. **文件上传下载**:系统可能包含文件上传功能,如用户上传书籍封面图片,或者管理员上传电子书。Servlet会处理文件上传...
综上所述,"ajax-demo推送演示例子"涉及到Ajax技术与Servlet服务器端推送的结合,通过长轮询实现页面实时更新的效果。开发者可以通过理解Ajax的工作机制,以及如何在服务器端实现推送,来构建类似的交互式Web应用。
本文将深入探讨如何利用Servlet 3.0的异步功能来构建一个推送信息至客户端的聊天室。 首先,我们需要了解Servlet 3.0的异步处理。在Servlet 2.x版本中,每次HTTP请求都会绑定到一个线程,直到请求处理完成。这种...
这些消息可能存储在一个消息队列中,当新的消息到来时,Java后端会将消息推送到连接的客户端。 JavaScript在前端起着接收和展示消息的作用。DWR提供了一种机制,使得JavaScript可以注册监听器,一旦后端有新的消息...
当用户发送消息时,后台接收并处理,然后通过AJAX回调将新消息推送到用户的浏览器,而无需刷新整个页面。 2. **动态更新**:例如,当有人加入或离开会议室时,用户界面可以即时显示这些变化,无需用户手动刷新。 3...
2. **实时接收**:服务器端接收到消息后,通过Ajax技术将新消息推送到客户端,更新聊天窗口,保持聊天的实时性。 3. **异步处理**:在用户查看或发送消息时,后台可以继续处理其他任务,如处理其他用户的消息,提高...
在Java中实现,可能需要设计用户接口、消息发布、关注/粉丝机制、消息推送等功能。这涉及到复杂的前后端交互和可能的实时更新技术,如WebSocket。 【聊天软件】 在Java Web环境中,聊天软件可能通过AJAX或者...
在“Java Dwr实现消息精准推送以及js调用后台+demo”这个主题中,我们将深入探讨如何利用DWR来实现高效的消息推送和JavaScript与后台Java的交互。以下是一些关键知识点: 1. **DWR基础**:DWR的核心功能是提供一种...
DWR(Direct Web Remoting...综上所述,这个"DWR消息推送简单实例"涵盖了DWR框架的基础使用、Maven工程的构建以及消息推送的实现。通过学习这个实例,开发者可以更好地理解如何在实际项目中利用DWR实现高效的双向通信。
或者,更高级的技术如WebSocket也可以被用到,提供双向通信,使服务器能主动推送新消息到客户端。 7. **安全性**:考虑到聊天室涉及用户交互,安全性至关重要。应防止SQL注入、XSS攻击等,对用户输入进行校验和过滤...
这个过程被称为“反向Ajax”或“服务器推送”,它能够实现在不依赖用户操作的情况下,服务器主动向客户端发送数据。 DWR的核心组件包括几个部分: 1. **Engine**:这是DWR的核心,负责处理JavaScript到Java的转换和...
为了实现消息推送,Servlet 3.0引入了异步处理能力。传统的Servlet模型是同步的,即每个请求都需要等待完成才能处理下一个请求,这对于实时应用来说效率低下。通过使用`AsyncContext`,我们可以启动一个后台线程来...
本项目是一个利用Ajax反向推送(Comet技术)实现的Web聊天室源码,通过Java语言的Servlet作为控制器,提供了一个完整的可运行示例。 首先,我们要理解什么是Ajax反向推送(Comet技术)。传统的Ajax请求是客户端发起...
DWR(Direct Web Remoting...总结来说,DWR是一种强大的工具,它利用AJAX技术实现了浏览器与服务器之间的双向通信,从而实现了消息推送功能。通过理解和熟练掌握DWR,开发者可以创建出更加互动、响应式的Web应用程序。
本文将详细探讨如何通过JAVA实现多种服务器推送客户端的方式,包括Ajax轮询、长连接、长轮询以及Iframe刷新。 1. **Ajax轮询** Ajax轮询是最基础的实现方式,其原理是客户端定时发送Ajax请求到服务器,服务器接收...
为了实现实时更新,前端需要定期(如每隔几秒)通过Ajax轮询Servlet获取新消息,或者使用更先进的技术如WebSocket,实现服务器向客户端的即时推送。 4. **Servlet技术**: Servlet是Java Web应用程序的一部分,...