1. 程式人生 > >spirng專案中整合webSocket踩到的坑

spirng專案中整合webSocket踩到的坑

 

 

花費了將近一週的時間終於將webSocket 整合到了原來的springmvc+spring +mybatis 專案中了,一路心酸一路的淚水啊,網上的技術文件都沒有解決我的問題,(很多文章都是你抄我的我抄你的)。

先來說一下我的環境:

windows  10, myeclipse 2014,orcale11 ,jdk 1.7.0.79 ,spring 4.3.8   springmvc 4.2.4.RELEASE  

 

websocket jar

           <dependency>
			    <groupId>javax.websocket</groupId>
			    <artifactId>javax.websocket-api</artifactId>
			    <version>1.0</version>
			    <scope>provided</scope>
			</dependency>
	        <dependency>
	            <groupId>org.springframework</groupId>
	            <artifactId>spring-websocket</artifactId>
	            <version>4.2.4.RELEASE</version>
	        </dependency>
	        <dependency>
	            <groupId>org.springframework</groupId>
	            <artifactId>spring-messaging</artifactId>
	            <version>4.2.4.RELEASE</version>
	        </dependency>	

下面就記錄一下自己遇到的問題,以及如何解決的問題

想貼上自己的程式碼

SpringWebSocketConfig.java  檔案

/**
 * 
 * 
 * @Description: TODO <p>SpringWebSocketConfig.java</p>
 * @作者: 王彥寶
 * @時間: 2018年10月24日下午3:52:57
 * @version V1.0
 * @see java.lang.Class
 * @since JDK{jdk1.7}
 */
//@EnableWebMvc//測試這個註解可以不要
@Configuration
@EnableWebSocket
public class SpringWebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer{

	
	@Override
	public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(webSocketHandler(),"/webSocketIMServer.json").addInterceptors(myInterceptor()).setAllowedOrigins("*");
        registry.addHandler(webSocketHandler(), "/sockjs/webSocketIMServer.json").withSockJS();
	}
	
	@Bean
    public SpringWebSocketHandler webSocketHandler(){
        return new SpringWebSocketHandler();
    }

	 @Bean
    public SpringWebSocketHandlerInterceptor myInterceptor(){
        return new SpringWebSocketHandlerInterceptor();
    }
}

 SpringWebSocketHandler.java  檔案

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

/**
 * 
 * 
 * @Description: TODO <p>SpringWebSocketHandler.java</p>
 * @作者: 王彥寶
 * @時間: 2018年10月24日下午3:55:49
 * @version V1.0
 * @see java.lang.Class
 * @since JDK{jdk1.7}
 */
public class SpringWebSocketHandler extends TextWebSocketHandler{
	
	private static final Logger LOGGER = LoggerFactory.getLogger(SpringWebSocketHandler.class);
	
	private static final ArrayList<WebSocketSession> users;//這個會出現效能問題,最好用Map來儲存,key用userid
	
	 static {
        users = new ArrayList<WebSocketSession>();
    }
	    
    public SpringWebSocketHandler() {
        // TODO Auto-generated constructor stub
    }
	
	/**
     * 連線成功時候,會觸發頁面上onopen方法
     */
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
    	LOGGER.info("connect to the websocket success......當前數量:"+users.size());
        users.add(session);
        //這塊會實現自己業務,比如,當用戶登入後,會把離線訊息推送給使用者,這裡需要查詢資料庫把待發送的訊息傳送給醫生
        TextMessage returnMessage = new TextMessage("你將收到的離線");
        session.sendMessage(returnMessage);
    }
    
   
   /**
    * 關閉連線時觸發
    */
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
        String username= (String) session.getAttributes().get("WEBSOCKET_EDU_SESSION_USERID");
        LOGGER.info("使用者"+username+"已退出!");
        users.remove(session);
        LOGGER.info("剩餘線上使用者"+users.size());
    }

    /**
     * 給某個使用者傳送訊息
     * @param userAndHosId
     * @param message
     * @作者: 王彥寶
     * @時間: 2018年11月30日下午1:51:32
     * @返回 void
     */
    public void sendMessageToUser(String username, TextMessage message) {
    	LOGGER.info("傳送訊息:"+message);
        for (WebSocketSession user : users) {
            if (user.getAttributes().get("WEBSOCKET_SESSION_USERID").equals(username)) {
                try {
                    if (user.isOpen()) {
                    	LOGGER.info("傳送訊息:"+message);
                        user.sendMessage(message);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                break;
            }
        }
    }
    
    /**
     * 群發所有線上使用者訊息
     *
     * @param userName
     * @param message
     */
    public void sendMessage(TextMessage message) {
    	LOGGER.info("傳送訊息:"+message);
        for (WebSocketSession user : users) {
            try {
                if (user.isOpen()) {
                	LOGGER.info("傳送訊息:"+message);
                    user.sendMessage(message);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    /**
     * js呼叫websocket.send時候,會呼叫該方法
     */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
 
        super.handleTextMessage(session, message);
 
        /**
         * 收到訊息,自定義處理機制,實現業務
         */
        System.out.println("伺服器收到訊息:"+message);
 
        if(message.getPayload().startsWith("#anyone#")){ //單發某人
 
             sendMessageToUser((String)session.getAttributes().get("WEBSOCKET_SESSION_USERID"), new TextMessage("伺服器單發:" +message.getPayload())) ;
 
        }else if(message.getPayload().startsWith("#everyone#")){
 
        	sendMessage(new TextMessage("伺服器群發:" +message.getPayload()));
 
        }else{
 
        }
 
    }
}

SpringWebSocketHandlerInterceptor.java 檔案

import java.util.Map;

import javax.servlet.http.HttpSession;

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.support.HttpSessionHandshakeInterceptor;

/**
 * 
 * WebSocket攔截器
 * @Description: TODO <p>SpringWebSocketHandlerInterceptor.java</p>
 * @作者: 王彥寶
 * @時間: 2018年10月24日下午3:58:34
 * @version V1.0
 * @see java.lang.Class
 * @since JDK{jdk1.7}
 */
public class SpringWebSocketHandlerInterceptor extends HttpSessionHandshakeInterceptor{

	
	@Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
            Map<String, Object> attributes) throws Exception {
        // TODO Auto-generated method stub
        System.out.println("Before Handshake");
        if (request instanceof ServletServerHttpRequest) {
            ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
            HttpSession session = servletRequest.getServletRequest().getSession(false);
            if (session != null) {
                //使用userName區分WebSocketHandler,以便定向傳送訊息
                String username = (String) session.getAttribute("SESSION_USERID");
                if (username==null) {
                	username="default-system";
                }
                attributes.put("WEBSOCKET_SESSION_USERID",username);
            }
        }
        return super.beforeHandshake(request, response, wsHandler, attributes);
        
    }
    
    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
            Exception ex) {
        // TODO Auto-generated method stub
        super.afterHandshake(request, response, wsHandler, ex);
    }
}

這就是核心的程式碼了。在spring  的配置檔案中加入  SpringWebSocketConfig  檔案的掃描路徑,然後啟動專案在前端進行訪問

ws://localhost:8081/XXXXX/webSocketIMServer.json");  這樣訪問就OK 了。

遇到的問題我總結一下:

1、404問題:

    在第一次訪問的時候報出了404  的錯誤,找不到路徑,最後發現問題在這裡

        registry.addHandler(webSocketHandler(),"/webSocketIMServer.json").addInterceptors(myInterceptor()).setAllowedOrigins("*");
        registry.addHandler(webSocketHandler(), "/sockjs/webSocketIMServer.json").withSockJS();

前端訪問的方法必須和後臺寫的這個方法名一致(包括字尾名,如果後臺程式碼沒有後綴,前端訪問時候就不要加字尾,不然就是訪問不到)"/webSocketIMServer.json"。

2、403問題:

    上面解決了404 的問題就又報出了403 的問題,然後我就在網上各種搜尋,有各種各樣的方案,都試過了,沒有解決問題,然後搜到了這個

部落格:https://blog.csdn.net/goxidono/article/details/53414897

和我的問題一模一樣,說的挺詳細,spring-webSocket.jar  裡面自動實現了HandshakeInterceptor  介面 

public class OriginHandshakeInterceptor implements HandshakeInterceptor {

 

然後再執行到這個方法的時候,this.interceptors   就會又兩個 interceptors,  第一個是我們自己實現的SpringWebSocketHandlerInterceptor  這個介面實現類,第二個就是   spring-webSocket  自動幫你實現的  OriginHandshakeInterceptor  這個介面實現類,

public boolean applyBeforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
			Map<String, Object> attributes) throws Exception {

		for (int i = 0; i < this.interceptors.size(); i++) {
			HandshakeInterceptor interceptor = this.interceptors.get(i);
			if (!interceptor.beforeHandshake(request, response, this.wsHandler, attributes)) {
				if (logger.isDebugEnabled()) {
					logger.debug(interceptor + " returns false from beforeHandshake - precluding handshake");
				}
				applyAfterHandshake(request, response, null);
				return false;
			}
			this.interceptorIndex = i;
		}
		return true;
	}

就在這個方法中會被攔截,然後我就改變了寫法,

下面是我剛開始的寫法:

public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        //WebIM WebSocket通道
        registry.addHandler(webSocketHandler(),"/webSocketIMServer").addInterceptors(myInterceptor());
        registry.addHandler(webSocketHandler(), "/sockjs/webSocketIMServer").addInterceptors(myInterceptor()).withSockJS();
    }

改變後的寫法:

public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
		//WebIM WebSocket通道
        registry.addHandler(webSocketHandler(),"/webSocketIMServer.json").addInterceptors(myInterceptor()).setAllowedOrigins("*");
        registry.addHandler(webSocketHandler(), "/sockjs/webSocketIMServer.json").withSockJS();
	}

然後繼續測試,發現還是不行,原因在執行到  applyBeforeHandshake 方法時有兩個攔截器,按照上面的寫法我們自己實現的攔截器時通過了,但是spring-webSocket 實現的攔截器沒有通過,然後又被攔截了報403 ,但是還是沒有解決我的問題。文章裡用的jdk 8 ,tomcat 8 ,和我的問題相似但是環境不同,用不了他的寫法,測試到這裡的時候我用的spirng 的版本  時4.2.4  ,然後我就想想換一個高點的spring  的版本試試看,之後就換了Spirng 4.3.8 的版本,啟動測試,居然OK了。好吧原來時版本的問題  最後的的環境是:

    

windows  10, myeclipse 2014,orcale11 ,jdk 1.7.0.79 ,spring 4.3.8   springmvc 4.2.4.RELEASE  

 

websocket jar

           <dependency>
			    <groupId>javax.websocket</groupId>
			    <artifactId>javax.websocket-api</artifactId>
			    <version>1.0</version>
			    <scope>provided</scope>
			</dependency>
	        <dependency>
	            <groupId>org.springframework</groupId>
	            <artifactId>spring-websocket</artifactId>
	            <version>4.2.4.RELEASE</version>
	        </dependency>
	        <dependency>
	            <groupId>org.springframework</groupId>
	            <artifactId>spring-messaging</artifactId>
	            <version>4.2.4.RELEASE</version>
	        </dependency>	

這樣就可以了。在此做一個記錄。