项目中有一个通讯模块,本来是用websocket全双工技术实现的,但IE10以下不支持websocket,而国内的360、2345浏
览器封装的全部是IE10以下的内核,考虑到网站在国内的客户,不得不在不支持websocket时候也要提供通讯支持,于
是决定在不支持websocket的浏览器上用long-pulling技术替代。
Servlet 3.0已经开始支持async,Spring MVC 3.2也开始对异步提供支持,于是结合DeferredResult来实现聊天技术。
假设你已经有了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>文件配置搞定。
/** * @作者 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); } } }
/** * @作者 yyp * @文件名 Message.java * @作用 封装用户的聊天内容 * @Blog http://blog.csdn.net/gisredevelopment */ public class Message { private String user; private String date; private String content; }
<!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>
<!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>