springmvc实现long-pulling技术

背景介绍:

项目中有一个通讯模块,本来是用websocket全双工技术实现的,但IE10以下不支持websocket,而国内的360、2345浏

览器封装的全部是IE10以下的内核,考虑到网站在国内的客户,不得不在不支持websocket时候也要提供通讯支持,于

是决定在不支持websocket的浏览器上用long-pulling技术替代。

可行性分析:

Servlet 3.0已经开始支持async,Spring MVC 3.2也开始对异步提供支持,于是结合DeferredResult来实现聊天技术。

具体实现:

1 文件配置:

假设你已经有了spring+springmvc框架,我们只需对配置文件做微小改动,要在web.xml中的所有的filter及servlet中需要声明使用async:

<async-supported>true</async-supported>

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"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	version="3.0">
	<!-- 配置spring-mybatis.xml -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring-mybatis.xml</param-value>
	</context-param>
	<filter>
		<filter-name>encodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<async-supported>true</async-supported>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>encodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	<!-- 配置spring-mvc -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	<listener>
		<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
	</listener>
	<servlet>
		<servlet-name>SpringMVC</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:spring-mvc.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
		<async-supported>true</async-supported>
	</servlet>
	<servlet-mapping>
		<servlet-name>SpringMVC</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>

</web-app>

文件配置搞定。

2 建立控制器Controller

/**
 * @作者 yyp
 * @文件名 ChatController.java
 * @作用 处理聊天消息
 * @Blog http://blog.csdn.net/gisredevelopment
 */
@Controller
public class ChatController {
	//存放所有的用户请求
	private final Map<String, DeferredResult<Message>> chatRequests = new ConcurrentHashMap<String, DeferredResult<Message>>();
	//时间格式化
	private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	/**
	 * @作者 yyp
	 * @作用 登录
	 * @param name 用户名
	 * @param session 会话
	 * @return 聊天室页面
	 */
	@RequestMapping(value = "/login", method = RequestMethod.POST)
	public String login(@RequestParam String name, HttpSession session){
		session.setAttribute("user", name);
		Message msg = new Message();
		msg.setUser("系统");
		msg.setDate(sdf.format(new Date()));
		msg.setContent(name + "已加入");
		//通知所有用户有人进入聊天室
		processMessage(msg);
		return "room";
	}
	/**
	 *
	 * @作者 yyp
	 * @作用 读取最新消息
	 * @param session 会话
	 * @return DeferredResult<Message>
	 */
	@RequestMapping(value = "/getMessages", method = RequestMethod.GET)
	@ResponseBody
	public DeferredResult<Message> getMessages(HttpSession session){
		//取出当前登录用户
		final String user = (String)session.getAttribute("user");
		//创建DeferredResult<Message>
		DeferredResult<Message> dr = new DeferredResult<Message>();
		//若用户不存在则直接返回,否则将其放入用户请求列表中然后返回
		if(null == user){
			return dr;
		}else{
			//当DeferredResult对客户端响应后将其从列表中移除
			dr.onCompletion(new Runnable() {
				@Override
				public void run() {
					// TODO 自动生成的方法存根
					chatRequests.remove(user);
				}
			});
			chatRequests.put(user, dr);
			return dr;
		}
	}
	/**
	 * @作者 yyp
	 * @作用 接收客户端消息
	 * @param session 会话
	 * @param content 消息内容
	 * @return Map<String, String>
	 */
	@RequestMapping(value = "/setMessage", method = RequestMethod.POST)
	@ResponseBody
	public Map<String, String> setMessage(HttpSession session, @RequestParam String content){
		Message msg = new Message();
		msg.setContent(content);
		msg.setDate(sdf.format(new Date()));
		msg.setUser((String)session.getAttribute("user"));
		//发布消息给所有用户
		processMessage(msg);
		Map<String, String> map = new HashMap<String, String>(1);
		map.put("success", "true");
		return map;
	}
	/**
	 * @作者 yyp
	 * @作用 退出聊天室
	 * @param session 会话
	 * @return Map<String, String>
	 */
	@RequestMapping(value = "/logout", method = RequestMethod.GET)
	@ResponseBody
	public Map<String, String> logout(HttpSession session){
		Message msg = new Message();
		String user = (String)session.getAttribute("user");
		msg.setContent("已离开");
		msg.setDate(sdf.format(new Date()));
		msg.setUser(user);
		chatRequests.remove(user);
		//通知所有用户有人离开聊天室
		processMessage(msg);
		Map<String, String> map = new HashMap<String, String>(1);
		map.put("success", "true");
		return map;
	}
	/**
	 * @作者 yyp
	 * @作用 将消息信息发布给所有在线用户
	 * @param msg 消息
	 */
	private void processMessage(Message msg){
		Set<String> keys = chatRequests.keySet();
		for(String key : keys){
			chatRequests.get(key).setResult(msg);
		}
	}
}

3 建立消息实体

/**
 * @作者 yyp
 * @文件名 Message.java
 * @作用 封装用户的聊天内容
 * @Blog http://blog.csdn.net/gisredevelopment
 */
public class Message {
	private String user;
	private String date;
	private String content;
}

4 页面代码-登录

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>登录</title>
</head>
<body>
<form action="login" method="post">
name: <input type="text" name="name"/>
<input value="登录" type="submit"/>
</form>
</body>
</html>

5 页面代码-聊天

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<% String user =(String)session.getAttribute("user"); %>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>聊天室</title>
<script type="text/javascript" src="/imgr?src=http%3A%2F%2Fwww.ineeke.com%2Farchives%2F1486%2Fjquery-1.10.1.min.js"></script>
<script type="text/javascript">
	$(function(){
		(function getMessages(){
			$.ajax({
				dataType: "json",
				url: 'getMessages',
				cache: false,
				success: function(data){
					var v = $('#text').val();
					v += '\r\n' + data.date + ' ' + data.user + ':' + data.content;
					$('#text').val(v);
				}
			}).always(function(){
				getMessages();
			});
		})();
		$('#form').submit(function(event){
			event.preventDefault();
			var values = $(this).serialize();
			$.post('setMessage', values, function(data){
				$('#form>[name=content]').val('');
			}, 'json');
		});
		$('#logout').click(function(){
			$.ajax({
				dataType: "json",
				url: 'logout',
				cache: false,
				success: function(data){
					window.location.href = 'index.jsp';
				}
			});
		});
	});
</script>
</head>
<body>
欢迎:<%=user %><br/>
<textarea id="text" rows="20" style="width: 500;"></textarea>
<form id="form" action="sendMessage" method="post">
<input type="text" name="content" />
<input value="发送" type="submit"/>
<input id="logout" value="离开" type="button"/>
</form>
</body>
</html>
时间: 2024-08-03 15:13:56

springmvc实现long-pulling技术的相关文章

SpringMVC 理论与有用技术(二)文件上传

文件上传相信大家都做过,差点儿全部的项目都有上传文件的功能,尤其是BS架构的项目中经常被列为常规功能来开发.不管是在开发.NET 项目还是java项目我们会用到非常多的框架,这个功能也被集成到了框架之中,由于他太经常使用了.框架就是为我们提供了一个开发项目的半成品结构.能够大大提高开发效率节省开发成本.缩短开发周期. 长话短说本文主要介绍一个怎么利用springmvc实现单个以及多个文件上传.附加说说上传文件的原理. 从详细的某个实例開始,先形似再神思 第一种方式设计思路:三个注意地方 1.表单

dubbo + springmvc + mybatis + ehcache + redis 技术介绍

Jeesz是一个分布式的框架,提供项目模块化.服务化.热插拔的思想,高度封装安全性的Java EE快速开发平台. Jeesz本身集成Dubbo服务管控.Zookeeper注册中心.Redis分布式缓存技术.FastDFS分布式文件系统.ActiveMQ异步消息中间件.Nginx负载均衡等分布式技术 使用Maven做项目管理,项目模块化,提高项目的易开发性.扩展性 以Spring Framework为核心容器,Spring MVC为模型视图控制器,MyBatis为数据访问层, Apache Shi

springmvc 返回xml

需求: 1.springmvc返回xml: 技术及环境: Spring 4.3.1.RELEASE JDK 1.8 IDEA 15.0.6 Maven 3 实现: spirngxml的配置主要如下: 添加项目依赖: <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.1.RELEASE

SpringMVC入门demo

为什么要学习SpringMVC呢? Spring框架号称是JavaEE应用的一站式解决方案,Spring本身有提供了一个设计优良的MVC框架,SpringMVC.由于Spring框架拥有极高的市场占有率,因此越来越多的Spring框架使用者使用SpringMVC代替曾经的MVC框架的王者Struts2.当然Spring也可以无缝地整合Struts.JSF等优秀的MVC框架. 越来越多的企业开始选择则SpringMVC+Mybatis来构建系统架构,而在电商热门的今天,SpringMVC+Myba

集成JPA+springmvc+spring+EJB中的Java EE应用

EJB是sun的JavaEE服务器端组件模型,设计目标与核心应用是部署分布式应用程序.凭借java跨平台的优势,用EJB技术部署的分布式系统可以不限于特定的平台.EJB (Enterprise JavaBean)是J2EE(javaEE)的一部分,定义了一个用于开发基于组件的企业多重应用程序的标准.其特点包括网络服务支持和核心开发工具(SDK). 在J2EE里,Enterprise Java Beans(EJB)称为Java 企业Bean,是Java的核心代码,分别是会话Bean(Session

浅谈SpringMVC执行过程

通过深入分析Spring源码,我们知道Spring框架包括大致六大模块, 如Web模块,数据库访问技术模块,面向切面模块,基础设施模块,核心容器模块和模块, 其中,在Spring框架的Web模块中,又包含很多前端技术,如SpringMVC,Spring WebSocket,Spring WebPortlet等,在本篇文章中,我们主要分析SpringMVC模块, 在分析SpringMVC技术时,本篇文章将通过解读SpringMVC源码,并通过编译器断点调试追踪,研究Spring核心类的UML图,来

SpringMVC执行原理

SpringMVC执行原理 听了秦老师的讲解受益匪浅,感谢秦老师 视频链接 图为SpringMVC的一个较完整的流程图,实线表示SpringMVC框架提供的技术,不需要开发者实现,虚线表示需要开发者实现. 自己分析 1.用户发起请求,经过前端控制器DispatcherServlet.2.DispatcherServlet根据请求找到对应映射器,3.映射器映射了谁,4.把这个映射返回,5.根据映射器再去适配这个映射器,6.适配的映射器说白了就是Controller,78.由具体Controller

挨踢部落坐诊第五期:数据采集与前端运用

活动说明:挨踢部落是为核心开发者提供深度技术交流,解决开发需求,资源共享的服务社群.基于此社群,我们邀请了业界技术大咖对开发需求进行一对一突破,解除开发过程中的绊脚石.以最专业.最高效的答复为开发者解决开发难题. 话题关键词:大数据来源/数据安全/云计算/Linux 部落阵容:51CTO社群智囊团 面向对象:Java工程师.大数据开发工程师.IT运维.数据分析师 参与方式:加入51CTO开发者QQ交流群(群号370892523(已满).请加312724475),有任何技术问题,在群里提问,或发给

HttpClient多文件上传

这辈子没办法做太多事情,所以每一件都要做到精彩绝伦! People can't do too many things in my life,so everything will be wonderful 项目使用技术SpringMVC + spring + mybatis HttpClient技术 1       HttpClientService工具类 该工具类封装了get.post.put.delete以及post上传多个文件等方法:包含了有无参数.日常开发,够用了! 一般来说,有些公司会有

求大哥们吐槽这个简历的缺陷

个人基本信息 男 教育背景 普通二本(学校名..) 群众 身高 178cm 本科 掌握外语 英语4级 手机号码 Email [email protected] 技能描述 1.   熟悉使用Oracle数据库(存储过程,触发器等).Mysql数据库,了解SQL Server数据库: 2.   熟悉使用Struts2.Spring.Hibernate.Mybaties.SpringMVC(了解).JQuery框架,能够组合使用SSH模式编写网站,熟悉Mybaties+SpringMVC模式以及ssi