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>




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章