最近一段时间因为需要,要做一个网页游戏。在某一段时间需要页面实时刷新。目前做网站普遍的思路都是js轮询的方式。由于是创新式的小项目,同组的好友提议了html5中提到的WebSocket,首先进行了技术调研。目前java方面支持的WebSocket的不算多。网上能搜到关于websocket实现框架的非常少。java EE7中加入WebSocket还没有看到。所以以来的包都来自于tomcat的支持。至少需要3个包tomcat-coyote,tomcat-catalina,tomcat-annotations-api,因为tomcat从7.027版本才开始比较好的支持websocket,在tomcat7.027之前的版本中,已经能使用websocket,但是会出现各式各样的问题。比如websocket连接后静置几秒钟就断开连接了等等。所以比较好的选择是使用7.027以上的版本。这3个jar包在相应tomcat的lib文件夹下都有。自从研究生期间接触maven后,慢慢了解到maven的强大,在这里不得不感叹一下。因为是个小型的敏捷团队,版本控制是必须的。在jar包控制这方面还是想通过maven来控制。就直接去maven中心库搜了搜。果真还是有的。小组讨论之后决定使用tomcat7.039(貌似40已经出了),到此解决了版本控制和jar包配置问题。pom关于tomcat3个jar包如下:
1 <dependency> 2 <groupId>org.apache.tomcat</groupId> 3 <artifactId>tomcat-coyote</artifactId> 4 <version>7.0.39</version> 5 </dependency> 6 <dependency> 7 <groupId>org.apache.tomcat</groupId> 8 <artifactId>tomcat-catalina</artifactId> 9 <version>7.0.39</version> 10 </dependency> 11 <dependency> 12 <groupId>org.apache.tomcat</groupId> 13 <artifactId>tomcat-annotations-api</artifactId> 14 <version>7.0.39</version> 15 </dependency>
接下来是解决架构问题。现在在网上能搜到关于websocket的实践很少,基本能搜到的都是websocket架构和非常简单的例子(tomcat自带就有websocket例子),怎么样能将websocket机制运用起来。首先基本框架准备使用hibernate+spring mvc结合websocket,但是在实际试验中spring mvc和websocket会有部分有冲突。因为到前一段时间Spring Framework 4.0发布的版本中才有了JDK 8的支持和WebSocket编程的支持。所以现阶段需要别的方式来实现spring mvc+websocket。简单的解决方案就是写一个工具类来手动获得bean。解决spring和websocket支持之后需要解决的websocket的交互方式。websocket最直接的两个方法就是onTextMessage和onBinaryMessage,也就是字节流传输和字符流传输。最优方式便是设计一套自己传输协议。通过字节流传输。前后台分别解析协议获取交互操作。其次便可在onTextMessage也就是字符流上做文章。引入json便可以很好支持了。
配置websocket的步骤:
1实现一个类继承ContextLoaderListener,并且在web.xml中配置
1 import javax.servlet.ServletContext; 2 import javax.servlet.ServletContextEvent; 3 4 import org.springframework.context.ApplicationContext; 5 import org.springframework.web.context.ContextLoaderListener; 6 import org.springframework.web.context.support.WebApplicationContextUtils; 7 8 public class SpringLoaderListener extends ContextLoaderListener{ 9 10 @Override 11 public void contextInitialized(ServletContextEvent event) { 12 super.contextInitialized(event); 13 ServletContext context=event.getServletContext(); 14 ApplicationContext ctx=WebApplicationContextUtils.getRequiredWebApplicationContext(context); 15 SpringContextutil.setContext(ctx); 16 17 } 18 19 }
web.xml
1 <listener> 2 <listener-class> 3 XXXXXX.utils.SpringLoaderListener 4 </listener-class> 5 </listener>
获得spring bean工具类:
1 import org.springframework.beans.BeansException; 2 import org.springframework.beans.factory.NoSuchBeanDefinitionException; 3 import org.springframework.context.ApplicationContext; 4 5 public class SpringContextUtil { 6 private static ApplicationContext context; 7 8 public static ApplicationContext getContext() { 9 return context; 10 } 11 12 public static void setContext(ApplicationContext context) { 13 SpringContextutil.context = context; 14 } 15 16 public static Object getBean(String name)throws BeansException{ 17 return context.getBean(name); 18 } 19 20 @SuppressWarnings("unchecked") 21 public static Object getBean(String name, Class requiredType) throws BeansException { 22 return context.getBean(name, requiredType); 23 } 24 25 public static boolean containsBean(String name) { 26 return context.containsBean(name); 27 } 28 29 public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { 30 return context.isSingleton(name); 31 } 32 33 @SuppressWarnings("unchecked") 34 public static Class getType(String name) throws NoSuchBeanDefinitionException { 35 return context.getType(name); 36 } 37 38 public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { 39 return context.getAliases(name); 40 } 41 42 43 }
引入json所需jar包:
1 <dependency> 2 <groupId>com.google.code.gson</groupId> 3 <artifactId>gson</artifactId> 4 <version>2.2.3</version> 5 </dependency> 6 <dependency> 7 <groupId>net.sf.json-lib</groupId> 8 <artifactId>json-lib</artifactId> 9 <version>2.4</version> 10 </dependency> 11 <dependency> 12 <groupId>net.sf.ezmorph</groupId> 13 <artifactId>ezmorph</artifactId> 14 <version>1.0.6</version> 15 </dependency>
后台需要增加两个文件一个继承WebSocketServlet:
1 import javax.servlet.annotation.WebServlet; 2 import javax.servlet.http.HttpServletRequest; 3 4 import org.apache.catalina.websocket.StreamInbound; 5 import org.apache.catalina.websocket.WebSocketServlet; 6 9 @WebServlet("/room") 10 public class RoomSocketServlet extends WebSocketServlet { 11 12 private static final long serialVersionUID = -5853470534275847275L; 13 14 @Override 15 protected StreamInbound createWebSocketInbound(String arg0,HttpServletRequest request) { 16 return new RoomMessageInbound(); 17 } 18 19 }
一个继承MessageInbound:
1 public class RoomMessageInbound extends MessageInbound{ 2 private static RoomModel room ; 3 private UserModel user; 4 private CommandDispatcherUtils commandDispatcher; 5 6 public RoomMessageInbound() { 7 if (commandDispatcher == null) { 8 commandDispatcher = (CommandDispatcherUtils) SpringContextutil.getBean("commandDispatcher"); 9 room = RoomListModel.getInstance().getRoom(0); 10 } 11 } 12 13 14 @Override 15 protected void onOpen(WsOutbound outbound) 16 room.addUser(outbound.hashCode()); 17 super.onOpen(outbound); 18 } 19 20 @Override 21 protected void onClose(int status) { 22 room.remove(getWsOutbound().hashCode()); 23 super.onClose(status); 24 } 25 26 @Override 27 protected void onBinaryMessage(ByteBuffer buffer) throws IOException { 28 29 } 30 31 @Override 32 protected void onTextMessage(CharBuffer buffer) throws IOException { 33 String msg = buffer.toString(); 34 JSONObject report = JSONObject.fromObject(msg); 35 TemplateCommand command = commandDispatcher.getCommandByKey(report.getString("command")); 36 command.execute(msg,user,room); 37 } 38 39 }
通过JSONObject report = JSONObject.fromObject(msg)就可以将字符串转换为json对象。也就等于通过websocket实现了实时对象信息的传递。
在前端页面中只需要页面载入时的js中加入
1 $(function() { 2 roomsocket = new WebSocket('ws://127.0.0.1:8080/XXXX/room); 3 }
在前端向后台发送时将数据JSON.stringify(data)就能json化。
上述代码做了不少删减。所以代码不见得复制就能用。只是提供一种解决方案。况且不久java新的版本或者说Spring Framework 4.0就能很轻松的支持了websocket的实现。主要是还是给自己这段时间的websocket研究做一些总结。随着各种新的技术的诞生,实时web技术已经越来越成熟。websocket是html5的重要特色是值得去看看,研究一下。