之所以sockjs會存在,說得不好聽點,就是因為微軟是個流氓,現在使用windows 7的系統仍然有近半,而windows 7預設內建的是ie 8,有些會自動更新到ie 9,但是大部分非IT使用者其實都不願意或者不會升級(通常我們做IT的認為很簡單的事情,在其他行業的人來看,那就是天書,不要覺得不可能,現實已如此)。
現在言歸正傳,這裡完整的講下在spring 4.x整合sockjs,以及運行在tomcat 7下時的一些額外注意事項。
spring websocket依賴jar:
<dependency> <groupId>javax.websocket</groupId> <artifactId>javax.websocket-api</artifactId> <version>1.1</version> <scope>provided</scope> <!-- 注意,scope必須為provided,否則runtime會衝突,如果使用tomcat 8,還需要將TOMCAT_HOME/lib下的javax.websocket-api.jar一併刪除 --> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-websocket</artifactId> <version>4.2.8.RELEASE</version> </dependency>
除非使用STOMP協議,否則不需要依賴spring-messaging。
spring通過兩種模式支援websocket,一種是通過原生websocket規範的ws://協議訪問(個人認為如果確定只用標準websocket訪問,還不如tomcat升級到8.x(tomcat 8原生支援JSR 356註解),spring的大量封裝畢竟增加了不少額外負載);另一種則是通過sockjs(也就是js)訪問,兩者目前暫時無法做到相容。
先完整說明第一種:
1、搭建spring mvc環境,這一點假設讀者已知;
2、pom.xml中引入上面兩個jar包;
3、spring支援websocket總共分為四個小步驟,handler、interceptor、config、web.xml,基本上可以認為spring mvc的翻版。
3.1、建立WebSocketHandler,spring支援兩種方式,一種是實現org.springframework.web.socket.WebSocketHandler介面,另外一種則是繼承TextWebSocketHandler或BinaryWebSocketHandler(現在大部分模板式架構或者外掛程式通常都是在提供了API的基礎上提供了抽象類別,把一些能統一的工作提前預置了,以便應用只需要關心業務,我們自己公司的中介軟體架構很多也是用這個模式實現了)。
/** * */package com.ld.net.spider.demo.ws;/** * @author zhjh256@163.com * {@link} http://www.cnblogs.com/zhjh256 */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;public class DemoWSHandler implements WebSocketHandler { @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { System.out.println("connect to the websocket success......"); session.sendMessage(new TextMessage("Server:connected OK!")); } @Override public void handleMessage(WebSocketSession wss, WebSocketMessage<?> wsm) throws Exception { TextMessage returnMessage = new TextMessage(wsm.getPayload() + " received at server"); System.out.println(wss.getHandshakeHeaders().getFirst("Cookie")); wss.sendMessage(returnMessage); } @Override public void handleTransportError(WebSocketSession wss, Throwable thrwbl) throws Exception { if(wss.isOpen()){ wss.close(); } System.out.println("websocket connection closed......"); } @Override public void afterConnectionClosed(WebSocketSession wss, CloseStatus cs) throws Exception { System.out.println("websocket connection closed......"); } @Override public boolean supportsPartialMessages() { return false; }}
3.2、建立攔截器
/** * */package com.ld.net.spider.demo.ws;import java.util.Map;import org.springframework.http.server.ServerHttpRequest;import org.springframework.http.server.ServerHttpResponse;import org.springframework.web.socket.WebSocketHandler;import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;/** * @author zhjh256@163.com * {@link} http://www.cnblogs.com/zhjh256 */public class HandshakeInterceptor extends HttpSessionHandshakeInterceptor { @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { // 解決The extension [x-webkit-deflate-frame] is not supported問題 if (request.getHeaders().containsKey("Sec-WebSocket-Extensions")) { request.getHeaders().set("Sec-WebSocket-Extensions", "permessage-deflate"); } System.out.println("Before Handshake"); return super.beforeHandshake(request, response, wsHandler, attributes); } @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception ex) { System.out.println("After Handshake"); super.afterHandshake(request, response, wsHandler, ex); }}
3.3、bean配置
<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:aop="http://www.springframework.org/schema/aop"xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:mongo="http://www.springframework.org/schema/data/mongo"xmlns:cache="http://www.springframework.org/schema/cache"xmlns:c="http://www.springframework.org/schema/c"xmlns:amq="http://activemq.apache.org/schema/core"xmlns:websocket="http://www.springframework.org/schema/websocket"xmlns:jms="http://www.springframework.org/schema/jms"xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/websockethttp://www.springframework.org/schema/websocket/spring-websocket.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc-3.2.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd http://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/cachehttp://www.springframework.org/schema/cache/spring-cache.xsdhttp://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsdhttp://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms.xsdhttp://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util.xsd"> <websocket:handlers allowed-origins="*"> <websocket:mapping path="/springws/websocket.ws" handler="demoWSHandler"/> <websocket:handshake-interceptors> <bean class="com.ld.net.spider.demo.ws.HandshakeInterceptor"/> </websocket:handshake-interceptors> </websocket:handlers> <bean id="demoWSHandler" class="com.ld.net.spider.demo.ws.DemoWSHandler"/>
上述需要注意的是,1、spring javadoc的說明是預設情況下,允許所有來源訪問,但我們跑下來發現不配置allowed-origins的話總是報403錯誤。
2、sockjs是不允許有尾碼的,否則將無法匹配,後面會專門講到。
3.4、web.xml配置
在web.xml中增加*.ws映射即可(如果原來不是/*的話),如下:
<servlet-mapping> <servlet-name>springMVC</servlet-name> <url-pattern>*.ws</url-pattern> </servlet-mapping>
上述配置完成之後,就可以通過標準的websocket介面進行訪問了,如下所示。
4、websocket用戶端
<!DOCTYPE html>