WebSocket


本文源代碼下載:spring_websocket

手冊文檔下載:websocket.doc

WebSocket_百度百科

Spring WebSocket簡單入門測試Demo(網頁及時聊天)

  項目需要一個及時推送消息的功能,並且這個任務落在了我的頭上,早有實現此功能的心思,這不機會就來了,網上看了下頁面ajax定時請求這種方案的居多,通過同時介紹找到了websocket,百度百科了下非常滿意,並輕松在網上找到了相關文章。下面簡單介紹下實現,代碼就不在這里貼了,Spring WebSocket簡單入門測試Demo(網頁及時聊天)這篇博文給的很全,下面只對實現過程中遇到的一些問題簡單說明下

問題1:建立鏈接失敗

  這個問題是由於頁面鏈接ip與地址欄中不一致所致,正確方式如下圖

  

  

 

  如果頁面寫成127.0.0.1,地址欄為localhost會鏈接報錯,相反也會報錯

問題2:多頁面使用websocket如何區分?

  這個問題困擾了好久,試過通過訪問鏈接、單獨建立websocket等方式,均不能很好的解決問題,最后還是通過萬能的“打debug”得到了解決思路,豁然開朗,原來如此簡單。解決思路如下:

  1)在WebsocketConfigure中為每個頁面添加單獨的websocket管理 

@Configuration
@EnableWebSocket//開啟websocket
public class WebSocketConfig implements WebSocketConfigurer {
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        //頁面1的websocket支持
        registry.addHandler(new WebSocketHander(), "/echo").addInterceptors(new HandshakeInterceptor()); //支持websocket 的訪問鏈接
        registry.addHandler(new WebSocketHander(), "/sockjs/echo").addInterceptors(new HandshakeInterceptor()).withSockJS(); //不支持websocket的訪問鏈接

        //頁面2的websocket支持
        registry.addHandler(new WebSocketHander(), "/echo1").addInterceptors(new HandshakeInterceptor()); //支持websocket 的訪問鏈接
        registry.addHandler(new WebSocketHander(), "/sockjs/echo1").addInterceptors(new HandshakeInterceptor()).withSockJS(); //不支持websocket的訪問鏈接
    }
}

  有原來的一個websocket變為兩個,只需與各自的頁面對應即可

  2)攔截器標識修改(粗體字為修改之處)

public class HandshakeInterceptor implements org.springframework.web.socket.server.HandshakeInterceptor {

    //進入hander之前的攔截
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse serverHttpResponse,
                                   WebSocketHandler webSocketHandler, Map<String, Object> map) throws Exception {
        String[] uri = request.getURI().toString().split("/"); if (request instanceof ServletServerHttpRequest) {
            ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
            HttpSession session = servletRequest.getServletRequest().getSession(true);
            //保證地址欄的請求和websocket的請求地址統一就能獲取到了
            User user = (User) session.getAttribute("now_user");
            if (session != null) {
                //使用userName區分WebSocketHandler,以便定向發送消息
                map.put("websocket_index", uri[uri.length - 1] + "_" + user.getUserName());
            }
        }
        return true;
    }

    public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse,
                               WebSocketHandler webSocketHandler, Exception e) {

    }
}

  3)消息發送方法代碼修改(黑體字為修改內容)

package com.controller.websocket;

import org.apache.log4j.Logger;
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;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by root on 16-10-26.
 */
public class WebSocketHander implements WebSocketHandler {

    private static Logger log = Logger.getLogger(WebSocketHander.class);
    private static int count = 0;//統計建立管道數
    private static final ArrayList<WebSocketSession> users = new ArrayList<WebSocketSession>();
    private static final Map<String, String> map = new HashMap(); //初次鏈接成功執行
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        log.debug("鏈接成功......");
        users.add(session);
        String key = (String) session.getAttributes().get("websocket_index");
        if (key != null) {
            //未讀消息處理邏輯
            count++;
            map.put(key, session.getId());
            session.sendMessage(new TextMessage(count + ""));
        }
    }

    //接受消息處理消息
    public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?> webSocketMessage) throws Exception {
        sendMessageToUsers(new TextMessage(webSocketMessage.getPayload() + ""));
        //sendMessageToUser("123", new TextMessage(webSocketMessage.getPayload() + ""));
    }

    public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable) throws Exception {
        if (webSocketSession.isOpen()) {
            webSocketSession.close();
        }
        count--;
        log.debug("鏈接出錯,關閉鏈接......");
        users.remove(webSocketSession);
    }

    //關閉或離開此頁面管道關閉
    public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception {
        count--;
        log.debug("鏈接關閉......" + closeStatus.toString());
        users.remove(webSocketSession);
    }

    public boolean supportsPartialMessages() {
        return false;
    }

    /**
     * 給所有在線用戶發送消息
     *
     * @param message
     */public void sendMessageToUsers1(String index, TextMessage message) { ArrayList<WebSocketSession> u = users; Map<String, String> m = map; for (WebSocketSession user : users) { try { String[] str = user.getAttributes().get("websocket_index").toString().split("_"); if (user.isOpen() && str[0].equals(index)) { user.sendMessage(message); } } catch (IOException e) { e.printStackTrace(); } } } /**
     * 給某個用戶發送消息
     *
     * @param userName
     * @param message
     */public void sendMessageToUser1(String index, TextMessage message) { ArrayList<WebSocketSession> u = users; Map<String, String> m = map; for (WebSocketSession user : users) { if (user.getId().equals(map.get(index))) { try { if (user.isOpen()) { user.sendMessage(message); } } catch (IOException e) { e.printStackTrace(); } break; } } } public static Map<String, String> getMap() { return map; }
}

  4)后端發送消息控制層代碼修改

package com.controller.websocket;

import com.entity.User;

import org.apache.log4j.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.socket.TextMessage;

import javax.servlet.http.HttpServletRequest;

/**
 * websocket數據推送測試 Created by root on 16-10-27.
 */ @Controller @RequestMapping(value = "/websocket") public class WebsocketController { private static Logger log = Logger.getLogger(WebsocketController.class); @Bean public WebSocketHander webSocketHandler() { return new WebSocketHander(); } /** * 后台推送消息給指定用戶 * * @param request * @return
     */ @RequestMapping("/auditing") @ResponseBody public String auditing(HttpServletRequest request, String index) { User user = (User) request.getSession().getAttribute("now_user"); //webSocketHandler().sendMessageToUser1(index + "_" + user.getUserName(), new TextMessage(user.getUserName()));
        webSocketHandler().sendMessageToUsers1(index, new TextMessage(user.getUserName())); return "success"; } /** * 打開此頁面前端和后端正式建立管道,關閉或離開此頁面管道關閉 * * @return
     */ @RequestMapping(value = "/websocket") public String websocket() { return "websocket"; } @RequestMapping(value = "/websocket1") public String websocket1() { return "websocket1"; }

}

 到此問題二得到解決。

問題3:同一頁面,在同一瀏覽器的兩個標簽中打開,此時同一功能的管道打開了兩個,如何在新的標簽打開頁面建立管道的時候關閉之前的?

  解決方案是在新標簽打開該頁面時,將之前頁面的管道關閉,代碼如下: 

//初次鏈接成功執行
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        String key = (String) session.getAttributes().get("websocket_index");
        users.add(session);
        log.debug(key + " 鏈接成功......");
        if (key != null) {
            if (map.get(key) != null) { for (WebSocketSession sess : users) { if (sess.getId().equals(map.get(key))) sess.close(); break; } } //未讀消息處理邏輯
            count++;
            map.put(key, session.getId());
            session.sendMessage(new TextMessage(count + ""));
        }
    }

 問題4:刷新頁面,之前管道未加載完的信息仍會在刷新后的頁面加載(要求刷新之后之前的請求停止)?

  解決方案記錄每個管道的WebSocketSession的id,保存到要發送消息的方法中,每次發送消息時檢測用戶的當前的管道id與記錄的是否相等,相等則發送,否則停止該方法。

  具體通過在WebSocketHander中定義map記錄每個用戶的管道信息實現。

  private static final Map<String, String> map = new HashMap();

最后給出下所需依賴
<!--websocket-->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>2.3.0</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.3.1</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.3.3</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-websocket</artifactId>
      <version>${org.springframework-version}</version>
    </dependency>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM