spring對websocket的集成和使用
WebSocket是HTML5提出的一個用於通信的協議規範,該協議通過一個握手機制,在客戶端和服務端之間建立一個類似於TCP的連接,從而方便客戶端和服務端之間的通信。
WebSocket協議本質上是一個基於TCP的協議,是先通過HTTP/HTTPS協議發起一條特殊的HTTP請求進行握手後創建一個用於交換數據的TCP連接,此後服務端與客戶端通過此TCP連接進行實時通信。客戶端和服務端只需要要做一個握手的動作,在建立連接之後,服務端和客戶端之間就可以通過此TCP連接進行實時通信。
websocket是建立在物理層上的連接,相比於基於網絡層的長連接可以節約資源,相比於AJAX輪訓可以降低服務器壓力。
spring在4.0後將websocket集成進去,要使用spring的websocket的話,spring的版本要在4.0及以上。
spring使用websocket需要的jar(pom.xml)
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-websocket</artifactId> <version>4.0.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId><artifactId>spring-messaging</artifactId> <version>4.0.6.RELEASE</version> </dependency>
web.xml中對websocket的配置
<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:/springMVC/spring-webSocket.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>
springMVC中對websocket的配置
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:websocket="http://www.springframework.org/schema/websocket" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.0.xsd"> <!-- websocket處理類 --> <bean id="msgHandler" class="com.test.websocket.MsgWebSocketHandler" /> <!-- 握手接口/攔截器 ,看項目需求是否需要--> <bean id="handshakeInterceptor" class="com.test.websocket.HandshakeInterceptor" /> <websocket:handlers> <websocket:mapping path="/websocket" handler="msgHandler" /> <websocket:handshake-interceptors> <ref bean="handshakeInterceptor" /> </websocket:handshake-interceptors> </websocket:handlers> <!-- 註冊 sockJS,sockJs是spring對不能使用websocket協議的客戶端提供一種模擬 --> <websocket:handlers> <websocket:mapping path="/sockjs/websocket" handler="msgHandler" /> <websocket:handshake-interceptors> <ref bean="handshakeInterceptor" /> </websocket:handshake-interceptors> <websocket:sockjs /> </websocket:handlers> </beans>
握手攔截器,繼承HttpSessionHandshakeInterceptor類,做一些連接握手或者握手後的一些處理
public class HandshakeInterceptor extends HttpSessionHandshakeInterceptor { // 握手前 @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { System.out .println("++++++++++++++++ HandshakeInterceptor: beforeHandshake ++++++++++++++" + attributes); return super.beforeHandshake(request, response, wsHandler, attributes); } // 握手後 @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception ex) { System.out .println("++++++++++++++++ HandshakeInterceptor: afterHandshake ++++++++++++++"); super.afterHandshake(request, response, wsHandler, ex); } }
創建MsgWebSocketHandler類繼承WebSocketHandler類(Spring提供的有AbstractWebSocketHandler類、TextWebSocketHandler類、BinaryWebSocketHandler類,看自己需要進行繼承),該類主要是用來處理消息的接收和發送
public class MsgWebSocketHandler implements WebSocketHandler { private Logger logger = LoggerFactory.getLogger(MsgWebSocketHandler.class); //保存用戶鏈接 private static ConcurrentHashMap<String, WebSocketSession> users = new ConcurrentHashMap<String, WebSocketSession>(); // 連接 就緒時 @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { users.put(session.getId(), session); } // 處理信息 @Override public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception { System.err.println(session + "---->" + message + ":"+ message.getPayload().toString()); } // 處理傳輸時異常 @Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { } // 關閉 連接時 @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) { logger.debug("用戶: " + session.getRemoteAddress() + " is leaving, because:" + closeStatus); } //是否支持分包 @Override public boolean supportsPartialMessages() { return false; } }
要進行發送消息的操作,自己可以寫方法,利用保存的已經完成連接的WebSocketSession,通過調用sendMessage(WebScoketMessage<?> message)方法進行消息的發送,參數message是發送的消息內容,Spring提供的類型有TextMessage、BinaryMessage、PingMessage、PongMessage。
前端客戶端JS
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>websocket</title> </head> <style type="text/css"> #div { color: red; } </style> <body> <h1>WEBSOCKET----TEST</h1> <div id="div"> </div> </script> <script type="text/javascript"> var div = document.getElementById(‘div‘); var socket = new WebSocket(‘ws://127.0.0.1:8088/websocket‘); socket.onopen = function(event){ console.log(event); socket.send(‘websocket client connect test‘); } socket.onclose = function(event){ console.log(event); } socket.onerror = function(event){ console.log(event); } socket.onmessage = function(event){ console.log(event) div.innerHTML += (‘ @_@ ‘ + event.data + ‘ ~_~ ‘); } </script> </body> </html>
其實在後臺也可以進行websocket客戶端的創建
@Test public void connectTest(){ WsWebSocketContainer wsWebSocketContainer = new WsWebSocketContainer(); wsWebSocketContainer.setDefaultMaxSessionIdleTimeout(300); StandardWebSocketClient client = new StandardWebSocketClient(wsWebSocketContainer); WebSocketHandler webSocketHandler = new MyWebSocketHandler(); String uriTemplate = "ws://127.0.0.1:8088/websocket?account=11111"; Object uriVars = null; ListenableFuture<WebSocketSession> future = client.doHandshake(webSocketHandler, uriTemplate, uriVars); try { WebSocketSession session = future.get(); session.sendMessage(new TextMessage("hello world")); } catch (InterruptedException | ExecutionException | IOException e) { e.printStackTrace(); } } @Test public void connectTest2(){ StandardWebSocketClient client = new StandardWebSocketClient(); WebSocketHandler webSocketHandler = new MyWebSocketHandler(); String uriTemplate = "ws://127.0.0.1:8088/websocket"; UriComponentsBuilder fromUriString = UriComponentsBuilder.fromUriString(uriTemplate); fromUriString.queryParam("account","111111"); /* * 作用同上,都是將請求參數填入到URI中 * MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>(); * params.add("account","111111"); * fromUriString.queryParams(params); * */ URI uri = fromUriString.buildAndExpand().encode().toUri(); WebSocketHttpHeaders headers = null; ListenableFuture<WebSocketSession> doHandshake = client.doHandshake(webSocketHandler, headers , uri); try { WebSocketSession session = doHandshake.get(); session.sendMessage(new TextMessage("hello world")); } catch (InterruptedException | ExecutionException | IOException e) { e.printStackTrace(); } }
這是我利用Junit進行的測試,實驗證明是可以完成websocket的連接。
註意:websocket在進行連接的時候是可以類似get請求一樣,將參數拼接到url中。還可以自己將參數封裝進URI中,利用另一個方法進行連接的握手操作。
spring對websocket的集成和使用