1. 程式人生 > >spring對websocket的集成和使用

spring對websocket的集成和使用

servlet ctype 基於 app set -c status att 實時通信

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的集成和使用