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 username
* @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对client响应后将其从列表中移除
dr.onCompletion(new Runnable() {
@Override
public void run() {
// TODO 自己主动生成的方法存根
chatRequests.remove(user);
}
});
chatRequests.put(user, dr);
return dr;
}
}
/**
* @作者 yyp
* @作用 接收client消息
* @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>
springmvc实现long-pulling技术的更多相关文章
- SpringMVC 理论与有用技术(一) 简单、有用、易懂的几个实例
SpringMVC先来看一下百度百科中的定义; Spring MVC属于SpringFrameWork的兴许产品,已经融合在Spring Web Flow里面.Spring 框架提供了构建 Web 应 ...
- SpringMVC+Spring+MyBatis个人技术博客源码
项目描述 Hi,大家好,又到了源码分享时间啦,今天我们分享的源码一个<个人技术博客>,该博客是基于SSM实现的一个个人博客系统,适合初学SSM和个人博客制作的同学学习.有了这个源码,直接买 ...
- SpringMVC 理论与有用技术(二)文件上传
文件上传相信大家都做过,差点儿全部的项目都有上传文件的功能,尤其是BS架构的项目中经常被列为常规功能来开发.不管是在开发.NET 项目还是java项目我们会用到非常多的框架,这个功能也被集成到了框架之 ...
- MyEclipse Spring 学习总结三 SpringMVC
MyEclipse Spring 学习总结三 SpringMVC 一.SpringMVC原理 1.Springmvc 框架介绍 1)Spring 框架停工了构建Web应用程序的全功能MVC模块.Spr ...
- springmvc 返回xml
需求: 1.springmvc返回xml: 技术及环境: Spring 4.3.1.RELEASE JDK 1.8 IDEA 15.0.6 Maven 3 实现: spirngxml的配置主要如下: ...
- 集成JPA+springmvc+spring+EJB中的Java EE应用
EJB是sun的JavaEE服务器端组件模型,设计目标与核心应用是部署分布式应用程序.凭借java跨平台的优势,用EJB技术部署的分布式系统可以不限于特定的平台.EJB (Enterprise Jav ...
- 浅谈SpringMVC执行过程
通过深入分析Spring源码,我们知道Spring框架包括大致六大模块, 如Web模块,数据库访问技术模块,面向切面模块,基础设施模块,核心容器模块和模块, 其中,在Spring框架的Web模块中,又 ...
- 一个简单的springmvc例子 入门(1)
一直是从事棋牌游戏,平常用的东西 大多数只是使用一些javase的一些 api对spring 这方面 用到的比较少,每次学了都忘,始终记不住.为了 更轻松学习springboot,从新学习了sprin ...
- SpringMVC 应知应会
springMVC 是表现层技术,可以用来代替 struts2,下面是简略图:主要是处理器和视图,只有这两个部分需要编写代码. springMVC 三大组件:处理器映射器,处理器适配器,视图解析器. ...
- SpringMVC笔记总结
文章所有代码见:gitee 1.回顾MVC 1.1.什么是MVC MVC是模型(Model).视图(View).控制器(Controller)的简写,是一种软件设计规范. 是将业务逻辑.数据.显示分离 ...
随机推荐
- 找出N之内的所有完数
时间限制: 10 Sec 内存限制: 128 MB 提交: 389 解决: 148 [提交][状态][讨论版] 题目描述 一个数如果恰好等于它的因子之和,这个数就称为"完数". ...
- [TroubleShooting] The server network address can not be reached or does not exist
Backtround: I'm trying to set up mirroring between two sql 2008 R2 databases on different servers in ...
- index_ss hint 使用的运行计划变化对照
index_ss hint 使用的运行计划变化对照 当中 buffer 代表:当前操作中发生的内存读次数,包括一致性读和当前读 尽管 emp 表记录数不多,可是buffer 读内存的次数区别还是有点 ...
- Oracle中merge into的使用 (转)
该命令使用一条语句从一个或者多个数据源中完成对表的更新和插入数据. ORACLE 9i 中,使用此命令必须同时指定UPDATE 和INSERT 关键词,ORACLE 10g 做了如下改动. 1.ins ...
- Java的socket服务UDP协议
练习1 接收类 package com.socket.demo; import java.io.IOException; import java.net.DatagramPacket; import ...
- RPG游戏学习——1.任务脚本系统
[前言] 近期准备做个rpg小游戏,所以開始研究rpg的一些系统.rpg最核心的应该是任务脚本系统(其它脚本系统类似),在參考了非常多网上的资料后,简要总结例如以下. [脚本的触发运行] 一个脚本须要 ...
- 09应用输入经理旋转场景--《猿学校课程Unity3d》
为什么极品飞车游戏等.,我们可以通过系统设置非常的方面根据自己喜欢的操作模式设置,有些人喜欢用箭头来控制不喜欢与使用"W,S,A,D"控制,这就解释程序猿不会死在程序写入内部控制, ...
- mapxtreme演示V1.3
mapxtreme演示V1.3 mapxtreme地图相关基本功能的演示其中包括 鹰眼地图,图层控制,发达,缩小,平移地图,地图模糊查询,中点工具,距离测量工具,面积测量工具,图元信息查看工具,各 ...
- 菜鸟nginx源码剖析 框架篇(一) 从main函数看nginx启动流程(转)
俗话说的好,牵牛要牵牛鼻子 驾车顶牛,处理复杂的东西,只要抓住重点,才能理清脉络,不至于深陷其中,不能自拔.对复杂的nginx而言,main函数就是“牛之鼻”,只要能理清main函数,就一定能理解其中 ...
- poj 1004 Dividing
大意是,从输入六个数 .第i个数代表价值为i的有几个,平均分给两个人 ,明摆着的背包问题,本来以为把他转化为01背包.可是TLe,后来发现是12万的平方还多,所以妥妥的TLE,后来发现这是一个全然背包 ...