友情提示: 消息推送的介绍可以参考http://www.cnblogs.com/dahuandan/p/6816173.html
什么是webSocket
webSocket是为解决客户端与服务端实时通信而产生的技术,其本质是使用一个TCP连接进行双向通讯,可以用来Web服务端的消息推送,被IETF定义为标准协议。
优点
相对于传统的HTTP轮询技术而言,webSocket是建立一次TCP连接进行客户端与服务端的双向通讯,减少了传统轮询技术频繁地向服务器发起请求,另外webSocket的Header相比HTTP的非常小。
helloWord
环境准备
Spring4.0版本以上
tomcat8
Constants.java
package demo; /** * webSocket常量 */ public class Constants { /** * http session 中 用户名的key值 */ public static String SESSION_USERNAME = "session_username"; /** * websocket session 中 用户名的key值 */ public static String WEBSOCKET_USERNAME = "websocket_username"; }
WebSocketConfig.java
package demo; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; /** * 注册websocket */ @Configuration @EnableWebMvc @EnableWebSocket public class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer{ public void registerWebSocketHandlers(WebSocketHandlerRegistry reg) { String websocket_url = "/webSocketServer"; //设置websocket的地址 reg.addHandler(systemWebSocketHandler(), websocket_url) //注册到Handler .addInterceptors(new WebSocketInterceptor()); //注册到Interceptor //提供SockJS支持(主要是兼容ie8) String sockjs_url = "/sockjs/webSocketServer"; //设置sockjs的地址 reg.addHandler(systemWebSocketHandler(),sockjs_url ) //注册到Handler .addInterceptors(new WebSocketInterceptor()) //注册到Interceptor .withSockJS(); //支持sockjs协议 } @Bean public WebSocketHandler systemWebSocketHandler(){ return new MyWebSocketHandler(); } }
WebSocketInterceptor.java
package demo; import java.util.Map; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.server.HandshakeInterceptor; /** * WebSocket握手拦截器 */ public class WebSocketInterceptor implements HandshakeInterceptor { //握手前 public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler, Map<String, Object> attr) throws Exception { if (request instanceof ServletServerHttpRequest) { ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request; String user = servletRequest.getServletRequest().getParameter("user"); attr.put(Constants.WEBSOCKET_USERNAME, user); } return true; } //握手后 public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler, Exception e) { } }
MyWebSocketHandler.java
package demo; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.WebSocketMessage; import org.springframework.web.socket.WebSocketSession; /** * WebSocket处理器 */ public class MyWebSocketHandler implements WebSocketHandler { /** * WebSocketSession容器 */ private static final List<WebSocketSession> users = new ArrayList<WebSocketSession>(); //建立连接后的函数 @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { users.add(session); } //消息处理 @Override public void handleMessage(WebSocketSession session, WebSocketMessage<?> ws_msg) throws Exception { // String user = (String)session.getAttributes().get(Constants.WEBSOCKET_USERNAME); sendMessageToUsers(new TextMessage("received at server: " + ws_msg.getPayload())); } //关闭连接后的函数 public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { users.remove(session); } //异常处理 @Override public void handleTransportError(WebSocketSession session, Throwable e) throws Exception { if(session.isOpen()) session.close(); users.remove(session); } @Override public boolean supportsPartialMessages() { return false; } /** * 给所有在线用户发送消息 * @param message * @throws IOException */ public void sendMessageToUsers(TextMessage message) throws IOException { for (WebSocketSession user : users) { if (user.isOpen()) user.sendMessage(message); } } /** * 给某个用户发送消息 * @param userName * @param message * @throws IOException */ public void sendMessageToUser(String userName, TextMessage message) throws IOException { for (WebSocketSession user : users) { if (user.getAttributes().get(Constants.WEBSOCKET_USERNAME).equals(userName)) { if (user.isOpen()) { user.sendMessage(message); } } } } }
index.jsp
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>websocket</title> <script src="jquery-1.10.2.js"></script> <script src="sockjs-0.3.min.js"></script> <script> $(function(){ var webSocket = newWebSocket(); sendMessage(webSocket); }); /** * 创建webSocket */ function newWebSocket(){ var webSocket; if ('WebSocket' in window) webSocket = new WebSocket("ws://localhost:8080/websocket/webSocketServer?user=123"); else if ('MozWebSocket' in window) webSocket = new MozWebSocket("ws://localhost:8080/websocket/webSocketServer"); else webSocket = new SockJS("http://localhost:8080/websocket/sockjs/webSocketServer"); //打开webSocket连接 webSocket.onopen = function (evnt) { }; //接收信息 webSocket.onmessage = function (evnt) { $("#console").append("(<font color='red'>"+evnt.data+"</font>)</br>") }; //错误处理 webSocket.onerror = function (evnt) { }; //关闭webSocket webSocket.onclose = function (evnt) { } return webSocket; } /** * 发送信息 */ function sendMessage(webSocket){ $("#send").click(function(){ webSocket.send($("#message").val()); }); } </script> </head> <body> <div> <textarea id="message" style=" 350px">send message!</textarea> </div> </br> <div> <button id="send">send</button> </div> <div id="console"></div> </body> </html>
spring.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> <context:annotation-config /> </beans>
springmvc.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd"> <mvc:annotation-driven/> <context:component-scan base-package="demo*"/> <!-- 静态资源解析 包括 :js、css、img、.. location: 资源所在的本地路径,建议不要放在webInf下 mapping :url的访问路径, **代表包含resource/路径后的所有子目录 --> <mvc:resources location="/" mapping="/**"/> </beans>
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <display-name>websocketDemo</display-name> <!-- 加载spring容器 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/classes/spring/spring.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- springmvc前端控制器,rest配置 --> <servlet> <servlet-name>springmvc_rest</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- contextConfigLocation配置springmvc加载的配置文件(配置处理器映射器、适配器等等) 如果不配置contextConfigLocation,默认加载的是/WEB-INF/servlet名称-serlvet.xml(springmvc-servlet.xml) --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/springmvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc_rest</servlet-name> <!-- 第一种:*.action,访问以.action结尾 由DispatcherServlet进行解析 第二种:/,所以访问的地址都由DispatcherServlet进行解析, 使用此种方式可以实现 RESTful风格的url(对于静态文件的解析需要配置不让DispatcherServlet进行解析) 第三种:/*,这样配置不对,使用这种配置,最终要转发到一个jsp页面时, 仍然会由DispatcherServlet解析jsp地址,不能根据jsp页面找到handler,会报错。 --> <url-pattern>/</url-pattern> </servlet-mapping> <!-- post乱码过虑器 --> <!-- 除了加过滤器,由于tomcat默认编码ISO-8859-1,还需要修改 %tomcat%/conf/server.xml Connector 标签加属性 URIEncoding="UTF-8" --> <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
测试
打开多个窗口,分别输入http://localhost:8080/websocket/index.jsp, 然后在某一窗口发送消息的时候,其他窗口都能接收到: